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  * Defines the interface and base operation of items that that can be
30  * dragged or can be drop targets.  It was designed to be extended, overriding
31  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
32  * Up to three html elements can be associated with a DragDrop instance:
33  * <ul>
34  * <li>linked element: the element that is passed into the constructor.
35  * This is the element which defines the boundaries for interaction with
36  * other DragDrop objects.</li>
37  * <li>handle element(s): The drag operation only occurs if the element that
38  * was clicked matches a handle element.  By default this is the linked
39  * element, but there are times that you will want only a portion of the
40  * linked element to initiate the drag operation, and the setHandleElId()
41  * method provides a way to define this.</li>
42  * <li>drag element: this represents the element that would be moved along
43  * with the cursor during a drag operation.  By default, this is the linked
44  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
45  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
46  * </li>
47  * </ul>
48  * This class should not be instantiated until the onload event to ensure that
49  * the associated elements are available.
50  * The following would define a DragDrop obj that would interact with any
51  * other DragDrop obj in the "group1" group:
52  * <pre>
53  *  dd = new Roo.dd.DragDrop("div1", "group1");
54  * </pre>
55  * Since none of the event handlers have been implemented, nothing would
56  * actually happen if you were to run the code above.  Normally you would
57  * override this class or one of the default implementations, but you can
58  * also override the methods you want on an instance of the class...
59  * <pre>
60  *  dd.onDragDrop = function(e, id) {
61  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
62  *  }
63  * </pre>
64  * @constructor
65  * @param {String} id of the element that is linked to this instance
66  * @param {String} sGroup the group of related DragDrop objects
67  * @param {object} config an object containing configurable attributes
68  *                Valid properties for DragDrop:
69  *                    padding, isTarget, maintainOffset, primaryButtonOnly
70  */
71 Roo.dd.DragDrop = function(id, sGroup, config) {
72     if (id) {
73         this.init(id, sGroup, config);
74     }
75 };
76
77 Roo.dd.DragDrop.prototype = {
78
79     /**
80      * The id of the element associated with this object.  This is what we
81      * refer to as the "linked element" because the size and position of
82      * this element is used to determine when the drag and drop objects have
83      * interacted.
84      * @property id
85      * @type String
86      */
87     id: null,
88
89     /**
90      * Configuration attributes passed into the constructor
91      * @property config
92      * @type object
93      */
94     config: null,
95
96     /**
97      * The id of the element that will be dragged.  By default this is same
98      * as the linked element , but could be changed to another element. Ex:
99      * Roo.dd.DDProxy
100      * @property dragElId
101      * @type String
102      * @private
103      */
104     dragElId: null,
105
106     /**
107      * the id of the element that initiates the drag operation.  By default
108      * this is the linked element, but could be changed to be a child of this
109      * element.  This lets us do things like only starting the drag when the
110      * header element within the linked html element is clicked.
111      * @property handleElId
112      * @type String
113      * @private
114      */
115     handleElId: null,
116
117     /**
118      * An associative array of HTML tags that will be ignored if clicked.
119      * @property invalidHandleTypes
120      * @type {string: string}
121      */
122     invalidHandleTypes: null,
123
124     /**
125      * An associative array of ids for elements that will be ignored if clicked
126      * @property invalidHandleIds
127      * @type {string: string}
128      */
129     invalidHandleIds: null,
130
131     /**
132      * An indexted array of css class names for elements that will be ignored
133      * if clicked.
134      * @property invalidHandleClasses
135      * @type string[]
136      */
137     invalidHandleClasses: null,
138
139     /**
140      * The linked element's absolute X position at the time the drag was
141      * started
142      * @property startPageX
143      * @type int
144      * @private
145      */
146     startPageX: 0,
147
148     /**
149      * The linked element's absolute X position at the time the drag was
150      * started
151      * @property startPageY
152      * @type int
153      * @private
154      */
155     startPageY: 0,
156
157     /**
158      * The group defines a logical collection of DragDrop objects that are
159      * related.  Instances only get events when interacting with other
160      * DragDrop object in the same group.  This lets us define multiple
161      * groups using a single DragDrop subclass if we want.
162      * @property groups
163      * @type {string: string}
164      */
165     groups: null,
166
167     /**
168      * Individual drag/drop instances can be locked.  This will prevent
169      * onmousedown start drag.
170      * @property locked
171      * @type boolean
172      * @private
173      */
174     locked: false,
175
176     /**
177      * Lock this instance
178      * @method lock
179      */
180     lock: function() { this.locked = true; },
181
182     /**
183      * Unlock this instace
184      * @method unlock
185      */
186     unlock: function() { this.locked = false; },
187
188     /**
189      * By default, all insances can be a drop target.  This can be disabled by
190      * setting isTarget to false.
191      * @method isTarget
192      * @type boolean
193      */
194     isTarget: true,
195
196     /**
197      * The padding configured for this drag and drop object for calculating
198      * the drop zone intersection with this object.
199      * @method padding
200      * @type int[]
201      */
202     padding: null,
203
204     /**
205      * Cached reference to the linked element
206      * @property _domRef
207      * @private
208      */
209     _domRef: null,
210
211     /**
212      * Internal typeof flag
213      * @property __ygDragDrop
214      * @private
215      */
216     __ygDragDrop: true,
217
218     /**
219      * Set to true when horizontal contraints are applied
220      * @property constrainX
221      * @type boolean
222      * @private
223      */
224     constrainX: false,
225
226     /**
227      * Set to true when vertical contraints are applied
228      * @property constrainY
229      * @type boolean
230      * @private
231      */
232     constrainY: false,
233
234     /**
235      * The left constraint
236      * @property minX
237      * @type int
238      * @private
239      */
240     minX: 0,
241
242     /**
243      * The right constraint
244      * @property maxX
245      * @type int
246      * @private
247      */
248     maxX: 0,
249
250     /**
251      * The up constraint
252      * @property minY
253      * @type int
254      * @type int
255      * @private
256      */
257     minY: 0,
258
259     /**
260      * The down constraint
261      * @property maxY
262      * @type int
263      * @private
264      */
265     maxY: 0,
266
267     /**
268      * Maintain offsets when we resetconstraints.  Set to true when you want
269      * the position of the element relative to its parent to stay the same
270      * when the page changes
271      *
272      * @property maintainOffset
273      * @type boolean
274      */
275     maintainOffset: false,
276
277     /**
278      * Array of pixel locations the element will snap to if we specified a
279      * horizontal graduation/interval.  This array is generated automatically
280      * when you define a tick interval.
281      * @property xTicks
282      * @type int[]
283      */
284     xTicks: null,
285
286     /**
287      * Array of pixel locations the element will snap to if we specified a
288      * vertical graduation/interval.  This array is generated automatically
289      * when you define a tick interval.
290      * @property yTicks
291      * @type int[]
292      */
293     yTicks: null,
294
295     /**
296      * By default the drag and drop instance will only respond to the primary
297      * button click (left button for a right-handed mouse).  Set to true to
298      * allow drag and drop to start with any mouse click that is propogated
299      * by the browser
300      * @property primaryButtonOnly
301      * @type boolean
302      */
303     primaryButtonOnly: true,
304
305     /**
306      * The availabe property is false until the linked dom element is accessible.
307      * @property available
308      * @type boolean
309      */
310     available: false,
311
312     /**
313      * By default, drags can only be initiated if the mousedown occurs in the
314      * region the linked element is.  This is done in part to work around a
315      * bug in some browsers that mis-report the mousedown if the previous
316      * mouseup happened outside of the window.  This property is set to true
317      * if outer handles are defined.
318      *
319      * @property hasOuterHandles
320      * @type boolean
321      * @default false
322      */
323     hasOuterHandles: false,
324
325     /**
326      * Code that executes immediately before the startDrag event
327      * @method b4StartDrag
328      * @private
329      */
330     b4StartDrag: function(x, y) { },
331
332     /**
333      * Abstract method called after a drag/drop object is clicked
334      * and the drag or mousedown time thresholds have beeen met.
335      * @method startDrag
336      * @param {int} X click location
337      * @param {int} Y click location
338      */
339     startDrag: function(x, y) { /* override this */ },
340
341     /**
342      * Code that executes immediately before the onDrag event
343      * @method b4Drag
344      * @private
345      */
346     b4Drag: function(e) { },
347
348     /**
349      * Abstract method called during the onMouseMove event while dragging an
350      * object.
351      * @method onDrag
352      * @param {Event} e the mousemove event
353      */
354     onDrag: function(e) { /* override this */ },
355
356     /**
357      * Abstract method called when this element fist begins hovering over
358      * another DragDrop obj
359      * @method onDragEnter
360      * @param {Event} e the mousemove event
361      * @param {String|DragDrop[]} id In POINT mode, the element
362      * id this is hovering over.  In INTERSECT mode, an array of one or more
363      * dragdrop items being hovered over.
364      */
365     onDragEnter: function(e, id) { /* override this */ },
366
367     /**
368      * Code that executes immediately before the onDragOver event
369      * @method b4DragOver
370      * @private
371      */
372     b4DragOver: function(e) { },
373
374     /**
375      * Abstract method called when this element is hovering over another
376      * DragDrop obj
377      * @method onDragOver
378      * @param {Event} e the mousemove event
379      * @param {String|DragDrop[]} id In POINT mode, the element
380      * id this is hovering over.  In INTERSECT mode, an array of dd items
381      * being hovered over.
382      */
383     onDragOver: function(e, id) { /* override this */ },
384
385     /**
386      * Code that executes immediately before the onDragOut event
387      * @method b4DragOut
388      * @private
389      */
390     b4DragOut: function(e) { },
391
392     /**
393      * Abstract method called when we are no longer hovering over an element
394      * @method onDragOut
395      * @param {Event} e the mousemove event
396      * @param {String|DragDrop[]} id In POINT mode, the element
397      * id this was hovering over.  In INTERSECT mode, an array of dd items
398      * that the mouse is no longer over.
399      */
400     onDragOut: function(e, id) { /* override this */ },
401
402     /**
403      * Code that executes immediately before the onDragDrop event
404      * @method b4DragDrop
405      * @private
406      */
407     b4DragDrop: function(e) { },
408
409     /**
410      * Abstract method called when this item is dropped on another DragDrop
411      * obj
412      * @method onDragDrop
413      * @param {Event} e the mouseup event
414      * @param {String|DragDrop[]} id In POINT mode, the element
415      * id this was dropped on.  In INTERSECT mode, an array of dd items this
416      * was dropped on.
417      */
418     onDragDrop: function(e, id) { /* override this */ },
419
420     /**
421      * Abstract method called when this item is dropped on an area with no
422      * drop target
423      * @method onInvalidDrop
424      * @param {Event} e the mouseup event
425      */
426     onInvalidDrop: function(e) { /* override this */ },
427
428     /**
429      * Code that executes immediately before the endDrag event
430      * @method b4EndDrag
431      * @private
432      */
433     b4EndDrag: function(e) { },
434
435     /**
436      * Fired when we are done dragging the object
437      * @method endDrag
438      * @param {Event} e the mouseup event
439      */
440     endDrag: function(e) { /* override this */ },
441
442     /**
443      * Code executed immediately before the onMouseDown event
444      * @method b4MouseDown
445      * @param {Event} e the mousedown event
446      * @private
447      */
448     b4MouseDown: function(e) {  },
449
450     /**
451      * Event handler that fires when a drag/drop obj gets a mousedown
452      * @method onMouseDown
453      * @param {Event} e the mousedown event
454      */
455     onMouseDown: function(e) { /* override this */ },
456
457     /**
458      * Event handler that fires when a drag/drop obj gets a mouseup
459      * @method onMouseUp
460      * @param {Event} e the mouseup event
461      */
462     onMouseUp: function(e) { /* override this */ },
463
464     /**
465      * Override the onAvailable method to do what is needed after the initial
466      * position was determined.
467      * @method onAvailable
468      */
469     onAvailable: function () {
470     },
471
472     /*
473      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
474      * @type Object
475      */
476     defaultPadding : {left:0, right:0, top:0, bottom:0},
477
478     /*
479      * Initializes the drag drop object's constraints to restrict movement to a certain element.
480  *
481  * Usage:
482  <pre><code>
483  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
484                 { dragElId: "existingProxyDiv" });
485  dd.startDrag = function(){
486      this.constrainTo("parent-id");
487  };
488  </code></pre>
489  * Or you can initalize it using the {@link Roo.Element} object:
490  <pre><code>
491  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
492      startDrag : function(){
493          this.constrainTo("parent-id");
494      }
495  });
496  </code></pre>
497      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
498      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
499      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
500      * an object containing the sides to pad. For example: {right:10, bottom:10}
501      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
502      */
503     constrainTo : function(constrainTo, pad, inContent){
504         if(typeof pad == "number"){
505             pad = {left: pad, right:pad, top:pad, bottom:pad};
506         }
507         pad = pad || this.defaultPadding;
508         var b = Roo.get(this.getEl()).getBox();
509         var ce = Roo.get(constrainTo);
510         var s = ce.getScroll();
511         var c, cd = ce.dom;
512         if(cd == document.body){
513             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
514         }else{
515             xy = ce.getXY();
516             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
517         }
518
519
520         var topSpace = b.y - c.y;
521         var leftSpace = b.x - c.x;
522
523         this.resetConstraints();
524         this.setXConstraint(leftSpace - (pad.left||0), // left
525                 c.width - leftSpace - b.width - (pad.right||0) //right
526         );
527         this.setYConstraint(topSpace - (pad.top||0), //top
528                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
529         );
530     },
531
532     /**
533      * Returns a reference to the linked element
534      * @method getEl
535      * @return {HTMLElement} the html element
536      */
537     getEl: function() {
538         if (!this._domRef) {
539             this._domRef = Roo.getDom(this.id);
540         }
541
542         return this._domRef;
543     },
544
545     /**
546      * Returns a reference to the actual element to drag.  By default this is
547      * the same as the html element, but it can be assigned to another
548      * element. An example of this can be found in Roo.dd.DDProxy
549      * @method getDragEl
550      * @return {HTMLElement} the html element
551      */
552     getDragEl: function() {
553         return Roo.getDom(this.dragElId);
554     },
555
556     /**
557      * Sets up the DragDrop object.  Must be called in the constructor of any
558      * Roo.dd.DragDrop subclass
559      * @method init
560      * @param id the id of the linked element
561      * @param {String} sGroup the group of related items
562      * @param {object} config configuration attributes
563      */
564     init: function(id, sGroup, config) {
565         this.initTarget(id, sGroup, config);
566         Event.on(this.id, "mousedown", this.handleMouseDown, this);
567         // Event.on(this.id, "selectstart", Event.preventDefault);
568     },
569
570     /**
571      * Initializes Targeting functionality only... the object does not
572      * get a mousedown handler.
573      * @method initTarget
574      * @param id the id of the linked element
575      * @param {String} sGroup the group of related items
576      * @param {object} config configuration attributes
577      */
578     initTarget: function(id, sGroup, config) {
579
580         // configuration attributes
581         this.config = config || {};
582
583         // create a local reference to the drag and drop manager
584         this.DDM = Roo.dd.DDM;
585         // initialize the groups array
586         this.groups = {};
587
588         // assume that we have an element reference instead of an id if the
589         // parameter is not a string
590         if (typeof id !== "string") {
591             id = Roo.id(id);
592         }
593
594         // set the id
595         this.id = id;
596
597         // add to an interaction group
598         this.addToGroup((sGroup) ? sGroup : "default");
599
600         // We don't want to register this as the handle with the manager
601         // so we just set the id rather than calling the setter.
602         this.handleElId = id;
603
604         // the linked element is the element that gets dragged by default
605         this.setDragElId(id);
606
607         // by default, clicked anchors will not start drag operations.
608         this.invalidHandleTypes = { A: "A" };
609         this.invalidHandleIds = {};
610         this.invalidHandleClasses = [];
611
612         this.applyConfig();
613
614         this.handleOnAvailable();
615     },
616
617     /**
618      * Applies the configuration parameters that were passed into the constructor.
619      * This is supposed to happen at each level through the inheritance chain.  So
620      * a DDProxy implentation will execute apply config on DDProxy, DD, and
621      * DragDrop in order to get all of the parameters that are available in
622      * each object.
623      * @method applyConfig
624      */
625     applyConfig: function() {
626
627         // configurable properties:
628         //    padding, isTarget, maintainOffset, primaryButtonOnly
629         this.padding           = this.config.padding || [0, 0, 0, 0];
630         this.isTarget          = (this.config.isTarget !== false);
631         this.maintainOffset    = (this.config.maintainOffset);
632         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
633
634     },
635
636     /**
637      * Executed when the linked element is available
638      * @method handleOnAvailable
639      * @private
640      */
641     handleOnAvailable: function() {
642         this.available = true;
643         this.resetConstraints();
644         this.onAvailable();
645     },
646
647      /**
648      * Configures the padding for the target zone in px.  Effectively expands
649      * (or reduces) the virtual object size for targeting calculations.
650      * Supports css-style shorthand; if only one parameter is passed, all sides
651      * will have that padding, and if only two are passed, the top and bottom
652      * will have the first param, the left and right the second.
653      * @method setPadding
654      * @param {int} iTop    Top pad
655      * @param {int} iRight  Right pad
656      * @param {int} iBot    Bot pad
657      * @param {int} iLeft   Left pad
658      */
659     setPadding: function(iTop, iRight, iBot, iLeft) {
660         // this.padding = [iLeft, iRight, iTop, iBot];
661         if (!iRight && 0 !== iRight) {
662             this.padding = [iTop, iTop, iTop, iTop];
663         } else if (!iBot && 0 !== iBot) {
664             this.padding = [iTop, iRight, iTop, iRight];
665         } else {
666             this.padding = [iTop, iRight, iBot, iLeft];
667         }
668     },
669
670     /**
671      * Stores the initial placement of the linked element.
672      * @method setInitialPosition
673      * @param {int} diffX   the X offset, default 0
674      * @param {int} diffY   the Y offset, default 0
675      */
676     setInitPosition: function(diffX, diffY) {
677         var el = this.getEl();
678
679         if (!this.DDM.verifyEl(el)) {
680             return;
681         }
682
683         var dx = diffX || 0;
684         var dy = diffY || 0;
685
686         var p = Dom.getXY( el );
687
688         this.initPageX = p[0] - dx;
689         this.initPageY = p[1] - dy;
690
691         this.lastPageX = p[0];
692         this.lastPageY = p[1];
693
694
695         this.setStartPosition(p);
696     },
697
698     /**
699      * Sets the start position of the element.  This is set when the obj
700      * is initialized, the reset when a drag is started.
701      * @method setStartPosition
702      * @param pos current position (from previous lookup)
703      * @private
704      */
705     setStartPosition: function(pos) {
706         var p = pos || Dom.getXY( this.getEl() );
707         this.deltaSetXY = null;
708
709         this.startPageX = p[0];
710         this.startPageY = p[1];
711     },
712
713     /**
714      * Add this instance to a group of related drag/drop objects.  All
715      * instances belong to at least one group, and can belong to as many
716      * groups as needed.
717      * @method addToGroup
718      * @param sGroup {string} the name of the group
719      */
720     addToGroup: function(sGroup) {
721         this.groups[sGroup] = true;
722         this.DDM.regDragDrop(this, sGroup);
723     },
724
725     /**
726      * Remove's this instance from the supplied interaction group
727      * @method removeFromGroup
728      * @param {string}  sGroup  The group to drop
729      */
730     removeFromGroup: function(sGroup) {
731         if (this.groups[sGroup]) {
732             delete this.groups[sGroup];
733         }
734
735         this.DDM.removeDDFromGroup(this, sGroup);
736     },
737
738     /**
739      * Allows you to specify that an element other than the linked element
740      * will be moved with the cursor during a drag
741      * @method setDragElId
742      * @param id {string} the id of the element that will be used to initiate the drag
743      */
744     setDragElId: function(id) {
745         this.dragElId = id;
746     },
747
748     /**
749      * Allows you to specify a child of the linked element that should be
750      * used to initiate the drag operation.  An example of this would be if
751      * you have a content div with text and links.  Clicking anywhere in the
752      * content area would normally start the drag operation.  Use this method
753      * to specify that an element inside of the content div is the element
754      * that starts the drag operation.
755      * @method setHandleElId
756      * @param id {string} the id of the element that will be used to
757      * initiate the drag.
758      */
759     setHandleElId: function(id) {
760         if (typeof id !== "string") {
761             id = Roo.id(id);
762         }
763         this.handleElId = id;
764         this.DDM.regHandle(this.id, id);
765     },
766
767     /**
768      * Allows you to set an element outside of the linked element as a drag
769      * handle
770      * @method setOuterHandleElId
771      * @param id the id of the element that will be used to initiate the drag
772      */
773     setOuterHandleElId: function(id) {
774         if (typeof id !== "string") {
775             id = Roo.id(id);
776         }
777         Event.on(id, "mousedown",
778                 this.handleMouseDown, this);
779         this.setHandleElId(id);
780
781         this.hasOuterHandles = true;
782     },
783
784     /**
785      * Remove all drag and drop hooks for this element
786      * @method unreg
787      */
788     unreg: function() {
789         Event.un(this.id, "mousedown",
790                 this.handleMouseDown);
791         this._domRef = null;
792         this.DDM._remove(this);
793     },
794
795     destroy : function(){
796         this.unreg();
797     },
798
799     /**
800      * Returns true if this instance is locked, or the drag drop mgr is locked
801      * (meaning that all drag/drop is disabled on the page.)
802      * @method isLocked
803      * @return {boolean} true if this obj or all drag/drop is locked, else
804      * false
805      */
806     isLocked: function() {
807         return (this.DDM.isLocked() || this.locked);
808     },
809
810     /**
811      * Fired when this object is clicked
812      * @method handleMouseDown
813      * @param {Event} e
814      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
815      * @private
816      */
817     handleMouseDown: function(e, oDD){
818         if (this.primaryButtonOnly && e.button != 0) {
819             return;
820         }
821
822         if (this.isLocked()) {
823             return;
824         }
825
826         this.DDM.refreshCache(this.groups);
827
828         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
829         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
830         } else {
831             if (this.clickValidator(e)) {
832
833                 // set the initial element position
834                 this.setStartPosition();
835
836
837                 this.b4MouseDown(e);
838                 this.onMouseDown(e);
839
840                 this.DDM.handleMouseDown(e, this);
841
842                 this.DDM.stopEvent(e);
843             } else {
844
845
846             }
847         }
848     },
849
850     clickValidator: function(e) {
851         var target = e.getTarget();
852         return ( this.isValidHandleChild(target) &&
853                     (this.id == this.handleElId ||
854                         this.DDM.handleWasClicked(target, this.id)) );
855     },
856
857     /**
858      * Allows you to specify a tag name that should not start a drag operation
859      * when clicked.  This is designed to facilitate embedding links within a
860      * drag handle that do something other than start the drag.
861      * @method addInvalidHandleType
862      * @param {string} tagName the type of element to exclude
863      */
864     addInvalidHandleType: function(tagName) {
865         var type = tagName.toUpperCase();
866         this.invalidHandleTypes[type] = type;
867     },
868
869     /**
870      * Lets you to specify an element id for a child of a drag handle
871      * that should not initiate a drag
872      * @method addInvalidHandleId
873      * @param {string} id the element id of the element you wish to ignore
874      */
875     addInvalidHandleId: function(id) {
876         if (typeof id !== "string") {
877             id = Roo.id(id);
878         }
879         this.invalidHandleIds[id] = id;
880     },
881
882     /**
883      * Lets you specify a css class of elements that will not initiate a drag
884      * @method addInvalidHandleClass
885      * @param {string} cssClass the class of the elements you wish to ignore
886      */
887     addInvalidHandleClass: function(cssClass) {
888         this.invalidHandleClasses.push(cssClass);
889     },
890
891     /**
892      * Unsets an excluded tag name set by addInvalidHandleType
893      * @method removeInvalidHandleType
894      * @param {string} tagName the type of element to unexclude
895      */
896     removeInvalidHandleType: function(tagName) {
897         var type = tagName.toUpperCase();
898         // this.invalidHandleTypes[type] = null;
899         delete this.invalidHandleTypes[type];
900     },
901
902     /**
903      * Unsets an invalid handle id
904      * @method removeInvalidHandleId
905      * @param {string} id the id of the element to re-enable
906      */
907     removeInvalidHandleId: function(id) {
908         if (typeof id !== "string") {
909             id = Roo.id(id);
910         }
911         delete this.invalidHandleIds[id];
912     },
913
914     /**
915      * Unsets an invalid css class
916      * @method removeInvalidHandleClass
917      * @param {string} cssClass the class of the element(s) you wish to
918      * re-enable
919      */
920     removeInvalidHandleClass: function(cssClass) {
921         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
922             if (this.invalidHandleClasses[i] == cssClass) {
923                 delete this.invalidHandleClasses[i];
924             }
925         }
926     },
927
928     /**
929      * Checks the tag exclusion list to see if this click should be ignored
930      * @method isValidHandleChild
931      * @param {HTMLElement} node the HTMLElement to evaluate
932      * @return {boolean} true if this is a valid tag type, false if not
933      */
934     isValidHandleChild: function(node) {
935
936         var valid = true;
937         // var n = (node.nodeName == "#text") ? node.parentNode : node;
938         var nodeName;
939         try {
940             nodeName = node.nodeName.toUpperCase();
941         } catch(e) {
942             nodeName = node.nodeName;
943         }
944         valid = valid && !this.invalidHandleTypes[nodeName];
945         valid = valid && !this.invalidHandleIds[node.id];
946
947         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
948             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
949         }
950
951
952         return valid;
953
954     },
955
956     /**
957      * Create the array of horizontal tick marks if an interval was specified
958      * in setXConstraint().
959      * @method setXTicks
960      * @private
961      */
962     setXTicks: function(iStartX, iTickSize) {
963         this.xTicks = [];
964         this.xTickSize = iTickSize;
965
966         var tickMap = {};
967
968         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
969             if (!tickMap[i]) {
970                 this.xTicks[this.xTicks.length] = i;
971                 tickMap[i] = true;
972             }
973         }
974
975         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
976             if (!tickMap[i]) {
977                 this.xTicks[this.xTicks.length] = i;
978                 tickMap[i] = true;
979             }
980         }
981
982         this.xTicks.sort(this.DDM.numericSort) ;
983     },
984
985     /**
986      * Create the array of vertical tick marks if an interval was specified in
987      * setYConstraint().
988      * @method setYTicks
989      * @private
990      */
991     setYTicks: function(iStartY, iTickSize) {
992         this.yTicks = [];
993         this.yTickSize = iTickSize;
994
995         var tickMap = {};
996
997         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
998             if (!tickMap[i]) {
999                 this.yTicks[this.yTicks.length] = i;
1000                 tickMap[i] = true;
1001             }
1002         }
1003
1004         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1005             if (!tickMap[i]) {
1006                 this.yTicks[this.yTicks.length] = i;
1007                 tickMap[i] = true;
1008             }
1009         }
1010
1011         this.yTicks.sort(this.DDM.numericSort) ;
1012     },
1013
1014     /**
1015      * By default, the element can be dragged any place on the screen.  Use
1016      * this method to limit the horizontal travel of the element.  Pass in
1017      * 0,0 for the parameters if you want to lock the drag to the y axis.
1018      * @method setXConstraint
1019      * @param {int} iLeft the number of pixels the element can move to the left
1020      * @param {int} iRight the number of pixels the element can move to the
1021      * right
1022      * @param {int} iTickSize optional parameter for specifying that the
1023      * element
1024      * should move iTickSize pixels at a time.
1025      */
1026     setXConstraint: function(iLeft, iRight, iTickSize) {
1027         this.leftConstraint = iLeft;
1028         this.rightConstraint = iRight;
1029
1030         this.minX = this.initPageX - iLeft;
1031         this.maxX = this.initPageX + iRight;
1032         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1033
1034         this.constrainX = true;
1035     },
1036
1037     /**
1038      * Clears any constraints applied to this instance.  Also clears ticks
1039      * since they can't exist independent of a constraint at this time.
1040      * @method clearConstraints
1041      */
1042     clearConstraints: function() {
1043         this.constrainX = false;
1044         this.constrainY = false;
1045         this.clearTicks();
1046     },
1047
1048     /**
1049      * Clears any tick interval defined for this instance
1050      * @method clearTicks
1051      */
1052     clearTicks: function() {
1053         this.xTicks = null;
1054         this.yTicks = null;
1055         this.xTickSize = 0;
1056         this.yTickSize = 0;
1057     },
1058
1059     /**
1060      * By default, the element can be dragged any place on the screen.  Set
1061      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1062      * parameters if you want to lock the drag to the x axis.
1063      * @method setYConstraint
1064      * @param {int} iUp the number of pixels the element can move up
1065      * @param {int} iDown the number of pixels the element can move down
1066      * @param {int} iTickSize optional parameter for specifying that the
1067      * element should move iTickSize pixels at a time.
1068      */
1069     setYConstraint: function(iUp, iDown, iTickSize) {
1070         this.topConstraint = iUp;
1071         this.bottomConstraint = iDown;
1072
1073         this.minY = this.initPageY - iUp;
1074         this.maxY = this.initPageY + iDown;
1075         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1076
1077         this.constrainY = true;
1078
1079     },
1080
1081     /**
1082      * resetConstraints must be called if you manually reposition a dd element.
1083      * @method resetConstraints
1084      * @param {boolean} maintainOffset
1085      */
1086     resetConstraints: function() {
1087
1088
1089         // Maintain offsets if necessary
1090         if (this.initPageX || this.initPageX === 0) {
1091             // figure out how much this thing has moved
1092             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1093             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1094
1095             this.setInitPosition(dx, dy);
1096
1097         // This is the first time we have detected the element's position
1098         } else {
1099             this.setInitPosition();
1100         }
1101
1102         if (this.constrainX) {
1103             this.setXConstraint( this.leftConstraint,
1104                                  this.rightConstraint,
1105                                  this.xTickSize        );
1106         }
1107
1108         if (this.constrainY) {
1109             this.setYConstraint( this.topConstraint,
1110                                  this.bottomConstraint,
1111                                  this.yTickSize         );
1112         }
1113     },
1114
1115     /**
1116      * Normally the drag element is moved pixel by pixel, but we can specify
1117      * that it move a number of pixels at a time.  This method resolves the
1118      * location when we have it set up like this.
1119      * @method getTick
1120      * @param {int} val where we want to place the object
1121      * @param {int[]} tickArray sorted array of valid points
1122      * @return {int} the closest tick
1123      * @private
1124      */
1125     getTick: function(val, tickArray) {
1126
1127         if (!tickArray) {
1128             // If tick interval is not defined, it is effectively 1 pixel,
1129             // so we return the value passed to us.
1130             return val;
1131         } else if (tickArray[0] >= val) {
1132             // The value is lower than the first tick, so we return the first
1133             // tick.
1134             return tickArray[0];
1135         } else {
1136             for (var i=0, len=tickArray.length; i<len; ++i) {
1137                 var next = i + 1;
1138                 if (tickArray[next] && tickArray[next] >= val) {
1139                     var diff1 = val - tickArray[i];
1140                     var diff2 = tickArray[next] - val;
1141                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1142                 }
1143             }
1144
1145             // The value is larger than the last tick, so we return the last
1146             // tick.
1147             return tickArray[tickArray.length - 1];
1148         }
1149     },
1150
1151     /**
1152      * toString method
1153      * @method toString
1154      * @return {string} string representation of the dd obj
1155      */
1156     toString: function() {
1157         return ("DragDrop " + this.id);
1158     }
1159
1160 };
1161
1162 })();
1163 /*
1164  * Based on:
1165  * Ext JS Library 1.1.1
1166  * Copyright(c) 2006-2007, Ext JS, LLC.
1167  *
1168  * Originally Released Under LGPL - original licence link has changed is not relivant.
1169  *
1170  * Fork - LGPL
1171  * <script type="text/javascript">
1172  */
1173
1174
1175 /**
1176  * The drag and drop utility provides a framework for building drag and drop
1177  * applications.  In addition to enabling drag and drop for specific elements,
1178  * the drag and drop elements are tracked by the manager class, and the
1179  * interactions between the various elements are tracked during the drag and
1180  * the implementing code is notified about these important moments.
1181  */
1182
1183 // Only load the library once.  Rewriting the manager class would orphan
1184 // existing drag and drop instances.
1185 if (!Roo.dd.DragDropMgr) {
1186
1187 /**
1188  * @class Roo.dd.DragDropMgr
1189  * DragDropMgr is a singleton that tracks the element interaction for
1190  * all DragDrop items in the window.  Generally, you will not call
1191  * this class directly, but it does have helper methods that could
1192  * be useful in your DragDrop implementations.
1193  * @singleton
1194  */
1195 Roo.dd.DragDropMgr = function() {
1196
1197     var Event = Roo.EventManager;
1198
1199     return {
1200
1201         /**
1202          * Two dimensional Array of registered DragDrop objects.  The first
1203          * dimension is the DragDrop item group, the second the DragDrop
1204          * object.
1205          * @property ids
1206          * @type {string: string}
1207          * @private
1208          * @static
1209          */
1210         ids: {},
1211
1212         /**
1213          * Array of element ids defined as drag handles.  Used to determine
1214          * if the element that generated the mousedown event is actually the
1215          * handle and not the html element itself.
1216          * @property handleIds
1217          * @type {string: string}
1218          * @private
1219          * @static
1220          */
1221         handleIds: {},
1222
1223         /**
1224          * the DragDrop object that is currently being dragged
1225          * @property dragCurrent
1226          * @type DragDrop
1227          * @private
1228          * @static
1229          **/
1230         dragCurrent: null,
1231
1232         /**
1233          * the DragDrop object(s) that are being hovered over
1234          * @property dragOvers
1235          * @type Array
1236          * @private
1237          * @static
1238          */
1239         dragOvers: {},
1240
1241         /**
1242          * the X distance between the cursor and the object being dragged
1243          * @property deltaX
1244          * @type int
1245          * @private
1246          * @static
1247          */
1248         deltaX: 0,
1249
1250         /**
1251          * the Y distance between the cursor and the object being dragged
1252          * @property deltaY
1253          * @type int
1254          * @private
1255          * @static
1256          */
1257         deltaY: 0,
1258
1259         /**
1260          * Flag to determine if we should prevent the default behavior of the
1261          * events we define. By default this is true, but this can be set to
1262          * false if you need the default behavior (not recommended)
1263          * @property preventDefault
1264          * @type boolean
1265          * @static
1266          */
1267         preventDefault: true,
1268
1269         /**
1270          * Flag to determine if we should stop the propagation of the events
1271          * we generate. This is true by default but you may want to set it to
1272          * false if the html element contains other features that require the
1273          * mouse click.
1274          * @property stopPropagation
1275          * @type boolean
1276          * @static
1277          */
1278         stopPropagation: true,
1279
1280         /**
1281          * Internal flag that is set to true when drag and drop has been
1282          * intialized
1283          * @property initialized
1284          * @private
1285          * @static
1286          */
1287         initalized: false,
1288
1289         /**
1290          * All drag and drop can be disabled.
1291          * @property locked
1292          * @private
1293          * @static
1294          */
1295         locked: false,
1296
1297         /**
1298          * Called the first time an element is registered.
1299          * @method init
1300          * @private
1301          * @static
1302          */
1303         init: function() {
1304             this.initialized = true;
1305         },
1306
1307         /**
1308          * In point mode, drag and drop interaction is defined by the
1309          * location of the cursor during the drag/drop
1310          * @property POINT
1311          * @type int
1312          * @static
1313          */
1314         POINT: 0,
1315
1316         /**
1317          * In intersect mode, drag and drop interactio nis defined by the
1318          * overlap of two or more drag and drop objects.
1319          * @property INTERSECT
1320          * @type int
1321          * @static
1322          */
1323         INTERSECT: 1,
1324
1325         /**
1326          * The current drag and drop mode.  Default: POINT
1327          * @property mode
1328          * @type int
1329          * @static
1330          */
1331         mode: 0,
1332
1333         /**
1334          * Runs method on all drag and drop objects
1335          * @method _execOnAll
1336          * @private
1337          * @static
1338          */
1339         _execOnAll: function(sMethod, args) {
1340             for (var i in this.ids) {
1341                 for (var j in this.ids[i]) {
1342                     var oDD = this.ids[i][j];
1343                     if (! this.isTypeOfDD(oDD)) {
1344                         continue;
1345                     }
1346                     oDD[sMethod].apply(oDD, args);
1347                 }
1348             }
1349         },
1350
1351         /**
1352          * Drag and drop initialization.  Sets up the global event handlers
1353          * @method _onLoad
1354          * @private
1355          * @static
1356          */
1357         _onLoad: function() {
1358
1359             this.init();
1360
1361
1362             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1363             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1364             Event.on(window,   "unload",    this._onUnload, this, true);
1365             Event.on(window,   "resize",    this._onResize, this, true);
1366             // Event.on(window,   "mouseout",    this._test);
1367
1368         },
1369
1370         /**
1371          * Reset constraints on all drag and drop objs
1372          * @method _onResize
1373          * @private
1374          * @static
1375          */
1376         _onResize: function(e) {
1377             this._execOnAll("resetConstraints", []);
1378         },
1379
1380         /**
1381          * Lock all drag and drop functionality
1382          * @method lock
1383          * @static
1384          */
1385         lock: function() { this.locked = true; },
1386
1387         /**
1388          * Unlock all drag and drop functionality
1389          * @method unlock
1390          * @static
1391          */
1392         unlock: function() { this.locked = false; },
1393
1394         /**
1395          * Is drag and drop locked?
1396          * @method isLocked
1397          * @return {boolean} True if drag and drop is locked, false otherwise.
1398          * @static
1399          */
1400         isLocked: function() { return this.locked; },
1401
1402         /**
1403          * Location cache that is set for all drag drop objects when a drag is
1404          * initiated, cleared when the drag is finished.
1405          * @property locationCache
1406          * @private
1407          * @static
1408          */
1409         locationCache: {},
1410
1411         /**
1412          * Set useCache to false if you want to force object the lookup of each
1413          * drag and drop linked element constantly during a drag.
1414          * @property useCache
1415          * @type boolean
1416          * @static
1417          */
1418         useCache: true,
1419
1420         /**
1421          * The number of pixels that the mouse needs to move after the
1422          * mousedown before the drag is initiated.  Default=3;
1423          * @property clickPixelThresh
1424          * @type int
1425          * @static
1426          */
1427         clickPixelThresh: 3,
1428
1429         /**
1430          * The number of milliseconds after the mousedown event to initiate the
1431          * drag if we don't get a mouseup event. Default=1000
1432          * @property clickTimeThresh
1433          * @type int
1434          * @static
1435          */
1436         clickTimeThresh: 350,
1437
1438         /**
1439          * Flag that indicates that either the drag pixel threshold or the
1440          * mousdown time threshold has been met
1441          * @property dragThreshMet
1442          * @type boolean
1443          * @private
1444          * @static
1445          */
1446         dragThreshMet: false,
1447
1448         /**
1449          * Timeout used for the click time threshold
1450          * @property clickTimeout
1451          * @type Object
1452          * @private
1453          * @static
1454          */
1455         clickTimeout: null,
1456
1457         /**
1458          * The X position of the mousedown event stored for later use when a
1459          * drag threshold is met.
1460          * @property startX
1461          * @type int
1462          * @private
1463          * @static
1464          */
1465         startX: 0,
1466
1467         /**
1468          * The Y position of the mousedown event stored for later use when a
1469          * drag threshold is met.
1470          * @property startY
1471          * @type int
1472          * @private
1473          * @static
1474          */
1475         startY: 0,
1476
1477         /**
1478          * Each DragDrop instance must be registered with the DragDropMgr.
1479          * This is executed in DragDrop.init()
1480          * @method regDragDrop
1481          * @param {DragDrop} oDD the DragDrop object to register
1482          * @param {String} sGroup the name of the group this element belongs to
1483          * @static
1484          */
1485         regDragDrop: function(oDD, sGroup) {
1486             if (!this.initialized) { this.init(); }
1487
1488             if (!this.ids[sGroup]) {
1489                 this.ids[sGroup] = {};
1490             }
1491             this.ids[sGroup][oDD.id] = oDD;
1492         },
1493
1494         /**
1495          * Removes the supplied dd instance from the supplied group. Executed
1496          * by DragDrop.removeFromGroup, so don't call this function directly.
1497          * @method removeDDFromGroup
1498          * @private
1499          * @static
1500          */
1501         removeDDFromGroup: function(oDD, sGroup) {
1502             if (!this.ids[sGroup]) {
1503                 this.ids[sGroup] = {};
1504             }
1505
1506             var obj = this.ids[sGroup];
1507             if (obj && obj[oDD.id]) {
1508                 delete obj[oDD.id];
1509             }
1510         },
1511
1512         /**
1513          * Unregisters a drag and drop item.  This is executed in
1514          * DragDrop.unreg, use that method instead of calling this directly.
1515          * @method _remove
1516          * @private
1517          * @static
1518          */
1519         _remove: function(oDD) {
1520             for (var g in oDD.groups) {
1521                 if (g && this.ids[g][oDD.id]) {
1522                     delete this.ids[g][oDD.id];
1523                 }
1524             }
1525             delete this.handleIds[oDD.id];
1526         },
1527
1528         /**
1529          * Each DragDrop handle element must be registered.  This is done
1530          * automatically when executing DragDrop.setHandleElId()
1531          * @method regHandle
1532          * @param {String} sDDId the DragDrop id this element is a handle for
1533          * @param {String} sHandleId the id of the element that is the drag
1534          * handle
1535          * @static
1536          */
1537         regHandle: function(sDDId, sHandleId) {
1538             if (!this.handleIds[sDDId]) {
1539                 this.handleIds[sDDId] = {};
1540             }
1541             this.handleIds[sDDId][sHandleId] = sHandleId;
1542         },
1543
1544         /**
1545          * Utility function to determine if a given element has been
1546          * registered as a drag drop item.
1547          * @method isDragDrop
1548          * @param {String} id the element id to check
1549          * @return {boolean} true if this element is a DragDrop item,
1550          * false otherwise
1551          * @static
1552          */
1553         isDragDrop: function(id) {
1554             return ( this.getDDById(id) ) ? true : false;
1555         },
1556
1557         /**
1558          * Returns the drag and drop instances that are in all groups the
1559          * passed in instance belongs to.
1560          * @method getRelated
1561          * @param {DragDrop} p_oDD the obj to get related data for
1562          * @param {boolean} bTargetsOnly if true, only return targetable objs
1563          * @return {DragDrop[]} the related instances
1564          * @static
1565          */
1566         getRelated: function(p_oDD, bTargetsOnly) {
1567             var oDDs = [];
1568             for (var i in p_oDD.groups) {
1569                 for (j in this.ids[i]) {
1570                     var dd = this.ids[i][j];
1571                     if (! this.isTypeOfDD(dd)) {
1572                         continue;
1573                     }
1574                     if (!bTargetsOnly || dd.isTarget) {
1575                         oDDs[oDDs.length] = dd;
1576                     }
1577                 }
1578             }
1579
1580             return oDDs;
1581         },
1582
1583         /**
1584          * Returns true if the specified dd target is a legal target for
1585          * the specifice drag obj
1586          * @method isLegalTarget
1587          * @param {DragDrop} the drag obj
1588          * @param {DragDrop} the target
1589          * @return {boolean} true if the target is a legal target for the
1590          * dd obj
1591          * @static
1592          */
1593         isLegalTarget: function (oDD, oTargetDD) {
1594             var targets = this.getRelated(oDD, true);
1595             for (var i=0, len=targets.length;i<len;++i) {
1596                 if (targets[i].id == oTargetDD.id) {
1597                     return true;
1598                 }
1599             }
1600
1601             return false;
1602         },
1603
1604         /**
1605          * My goal is to be able to transparently determine if an object is
1606          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1607          * returns "object", oDD.constructor.toString() always returns
1608          * "DragDrop" and not the name of the subclass.  So for now it just
1609          * evaluates a well-known variable in DragDrop.
1610          * @method isTypeOfDD
1611          * @param {Object} the object to evaluate
1612          * @return {boolean} true if typeof oDD = DragDrop
1613          * @static
1614          */
1615         isTypeOfDD: function (oDD) {
1616             return (oDD && oDD.__ygDragDrop);
1617         },
1618
1619         /**
1620          * Utility function to determine if a given element has been
1621          * registered as a drag drop handle for the given Drag Drop object.
1622          * @method isHandle
1623          * @param {String} id the element id to check
1624          * @return {boolean} true if this element is a DragDrop handle, false
1625          * otherwise
1626          * @static
1627          */
1628         isHandle: function(sDDId, sHandleId) {
1629             return ( this.handleIds[sDDId] &&
1630                             this.handleIds[sDDId][sHandleId] );
1631         },
1632
1633         /**
1634          * Returns the DragDrop instance for a given id
1635          * @method getDDById
1636          * @param {String} id the id of the DragDrop object
1637          * @return {DragDrop} the drag drop object, null if it is not found
1638          * @static
1639          */
1640         getDDById: function(id) {
1641             for (var i in this.ids) {
1642                 if (this.ids[i][id]) {
1643                     return this.ids[i][id];
1644                 }
1645             }
1646             return null;
1647         },
1648
1649         /**
1650          * Fired after a registered DragDrop object gets the mousedown event.
1651          * Sets up the events required to track the object being dragged
1652          * @method handleMouseDown
1653          * @param {Event} e the event
1654          * @param oDD the DragDrop object being dragged
1655          * @private
1656          * @static
1657          */
1658         handleMouseDown: function(e, oDD) {
1659             if(Roo.QuickTips){
1660                 Roo.QuickTips.disable();
1661             }
1662             this.currentTarget = e.getTarget();
1663
1664             this.dragCurrent = oDD;
1665
1666             var el = oDD.getEl();
1667
1668             // track start position
1669             this.startX = e.getPageX();
1670             this.startY = e.getPageY();
1671
1672             this.deltaX = this.startX - el.offsetLeft;
1673             this.deltaY = this.startY - el.offsetTop;
1674
1675             this.dragThreshMet = false;
1676
1677             this.clickTimeout = setTimeout(
1678                     function() {
1679                         var DDM = Roo.dd.DDM;
1680                         DDM.startDrag(DDM.startX, DDM.startY);
1681                     },
1682                     this.clickTimeThresh );
1683         },
1684
1685         /**
1686          * Fired when either the drag pixel threshol or the mousedown hold
1687          * time threshold has been met.
1688          * @method startDrag
1689          * @param x {int} the X position of the original mousedown
1690          * @param y {int} the Y position of the original mousedown
1691          * @static
1692          */
1693         startDrag: function(x, y) {
1694             clearTimeout(this.clickTimeout);
1695             if (this.dragCurrent) {
1696                 this.dragCurrent.b4StartDrag(x, y);
1697                 this.dragCurrent.startDrag(x, y);
1698             }
1699             this.dragThreshMet = true;
1700         },
1701
1702         /**
1703          * Internal function to handle the mouseup event.  Will be invoked
1704          * from the context of the document.
1705          * @method handleMouseUp
1706          * @param {Event} e the event
1707          * @private
1708          * @static
1709          */
1710         handleMouseUp: function(e) {
1711
1712             if(Roo.QuickTips){
1713                 Roo.QuickTips.enable();
1714             }
1715             if (! this.dragCurrent) {
1716                 return;
1717             }
1718
1719             clearTimeout(this.clickTimeout);
1720
1721             if (this.dragThreshMet) {
1722                 this.fireEvents(e, true);
1723             } else {
1724             }
1725
1726             this.stopDrag(e);
1727
1728             this.stopEvent(e);
1729         },
1730
1731         /**
1732          * Utility to stop event propagation and event default, if these
1733          * features are turned on.
1734          * @method stopEvent
1735          * @param {Event} e the event as returned by this.getEvent()
1736          * @static
1737          */
1738         stopEvent: function(e){
1739             if(this.stopPropagation) {
1740                 e.stopPropagation();
1741             }
1742
1743             if (this.preventDefault) {
1744                 e.preventDefault();
1745             }
1746         },
1747
1748         /**
1749          * Internal function to clean up event handlers after the drag
1750          * operation is complete
1751          * @method stopDrag
1752          * @param {Event} e the event
1753          * @private
1754          * @static
1755          */
1756         stopDrag: function(e) {
1757             // Fire the drag end event for the item that was dragged
1758             if (this.dragCurrent) {
1759                 if (this.dragThreshMet) {
1760                     this.dragCurrent.b4EndDrag(e);
1761                     this.dragCurrent.endDrag(e);
1762                 }
1763
1764                 this.dragCurrent.onMouseUp(e);
1765             }
1766
1767             this.dragCurrent = null;
1768             this.dragOvers = {};
1769         },
1770
1771         /**
1772          * Internal function to handle the mousemove event.  Will be invoked
1773          * from the context of the html element.
1774          *
1775          * @TODO figure out what we can do about mouse events lost when the
1776          * user drags objects beyond the window boundary.  Currently we can
1777          * detect this in internet explorer by verifying that the mouse is
1778          * down during the mousemove event.  Firefox doesn't give us the
1779          * button state on the mousemove event.
1780          * @method handleMouseMove
1781          * @param {Event} e the event
1782          * @private
1783          * @static
1784          */
1785         handleMouseMove: function(e) {
1786             if (! this.dragCurrent) {
1787                 return true;
1788             }
1789
1790             // var button = e.which || e.button;
1791
1792             // check for IE mouseup outside of page boundary
1793             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1794                 this.stopEvent(e);
1795                 return this.handleMouseUp(e);
1796             }
1797
1798             if (!this.dragThreshMet) {
1799                 var diffX = Math.abs(this.startX - e.getPageX());
1800                 var diffY = Math.abs(this.startY - e.getPageY());
1801                 if (diffX > this.clickPixelThresh ||
1802                             diffY > this.clickPixelThresh) {
1803                     this.startDrag(this.startX, this.startY);
1804                 }
1805             }
1806
1807             if (this.dragThreshMet) {
1808                 this.dragCurrent.b4Drag(e);
1809                 this.dragCurrent.onDrag(e);
1810                 if(!this.dragCurrent.moveOnly){
1811                     this.fireEvents(e, false);
1812                 }
1813             }
1814
1815             this.stopEvent(e);
1816
1817             return true;
1818         },
1819
1820         /**
1821          * Iterates over all of the DragDrop elements to find ones we are
1822          * hovering over or dropping on
1823          * @method fireEvents
1824          * @param {Event} e the event
1825          * @param {boolean} isDrop is this a drop op or a mouseover op?
1826          * @private
1827          * @static
1828          */
1829         fireEvents: function(e, isDrop) {
1830             var dc = this.dragCurrent;
1831
1832             // If the user did the mouse up outside of the window, we could
1833             // get here even though we have ended the drag.
1834             if (!dc || dc.isLocked()) {
1835                 return;
1836             }
1837
1838             var pt = e.getPoint();
1839
1840             // cache the previous dragOver array
1841             var oldOvers = [];
1842
1843             var outEvts   = [];
1844             var overEvts  = [];
1845             var dropEvts  = [];
1846             var enterEvts = [];
1847
1848             // Check to see if the object(s) we were hovering over is no longer
1849             // being hovered over so we can fire the onDragOut event
1850             for (var i in this.dragOvers) {
1851
1852                 var ddo = this.dragOvers[i];
1853
1854                 if (! this.isTypeOfDD(ddo)) {
1855                     continue;
1856                 }
1857
1858                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1859                     outEvts.push( ddo );
1860                 }
1861
1862                 oldOvers[i] = true;
1863                 delete this.dragOvers[i];
1864             }
1865
1866             for (var sGroup in dc.groups) {
1867
1868                 if ("string" != typeof sGroup) {
1869                     continue;
1870                 }
1871
1872                 for (i in this.ids[sGroup]) {
1873                     var oDD = this.ids[sGroup][i];
1874                     if (! this.isTypeOfDD(oDD)) {
1875                         continue;
1876                     }
1877
1878                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1879                         if (this.isOverTarget(pt, oDD, this.mode)) {
1880                             // look for drop interactions
1881                             if (isDrop) {
1882                                 dropEvts.push( oDD );
1883                             // look for drag enter and drag over interactions
1884                             } else {
1885
1886                                 // initial drag over: dragEnter fires
1887                                 if (!oldOvers[oDD.id]) {
1888                                     enterEvts.push( oDD );
1889                                 // subsequent drag overs: dragOver fires
1890                                 } else {
1891                                     overEvts.push( oDD );
1892                                 }
1893
1894                                 this.dragOvers[oDD.id] = oDD;
1895                             }
1896                         }
1897                     }
1898                 }
1899             }
1900
1901             if (this.mode) {
1902                 if (outEvts.length) {
1903                     dc.b4DragOut(e, outEvts);
1904                     dc.onDragOut(e, outEvts);
1905                 }
1906
1907                 if (enterEvts.length) {
1908                     dc.onDragEnter(e, enterEvts);
1909                 }
1910
1911                 if (overEvts.length) {
1912                     dc.b4DragOver(e, overEvts);
1913                     dc.onDragOver(e, overEvts);
1914                 }
1915
1916                 if (dropEvts.length) {
1917                     dc.b4DragDrop(e, dropEvts);
1918                     dc.onDragDrop(e, dropEvts);
1919                 }
1920
1921             } else {
1922                 // fire dragout events
1923                 var len = 0;
1924                 for (i=0, len=outEvts.length; i<len; ++i) {
1925                     dc.b4DragOut(e, outEvts[i].id);
1926                     dc.onDragOut(e, outEvts[i].id);
1927                 }
1928
1929                 // fire enter events
1930                 for (i=0,len=enterEvts.length; i<len; ++i) {
1931                     // dc.b4DragEnter(e, oDD.id);
1932                     dc.onDragEnter(e, enterEvts[i].id);
1933                 }
1934
1935                 // fire over events
1936                 for (i=0,len=overEvts.length; i<len; ++i) {
1937                     dc.b4DragOver(e, overEvts[i].id);
1938                     dc.onDragOver(e, overEvts[i].id);
1939                 }
1940
1941                 // fire drop events
1942                 for (i=0, len=dropEvts.length; i<len; ++i) {
1943                     dc.b4DragDrop(e, dropEvts[i].id);
1944                     dc.onDragDrop(e, dropEvts[i].id);
1945                 }
1946
1947             }
1948
1949             // notify about a drop that did not find a target
1950             if (isDrop && !dropEvts.length) {
1951                 dc.onInvalidDrop(e);
1952             }
1953
1954         },
1955
1956         /**
1957          * Helper function for getting the best match from the list of drag
1958          * and drop objects returned by the drag and drop events when we are
1959          * in INTERSECT mode.  It returns either the first object that the
1960          * cursor is over, or the object that has the greatest overlap with
1961          * the dragged element.
1962          * @method getBestMatch
1963          * @param  {DragDrop[]} dds The array of drag and drop objects
1964          * targeted
1965          * @return {DragDrop}       The best single match
1966          * @static
1967          */
1968         getBestMatch: function(dds) {
1969             var winner = null;
1970             // Return null if the input is not what we expect
1971             //if (!dds || !dds.length || dds.length == 0) {
1972                // winner = null;
1973             // If there is only one item, it wins
1974             //} else if (dds.length == 1) {
1975
1976             var len = dds.length;
1977
1978             if (len == 1) {
1979                 winner = dds[0];
1980             } else {
1981                 // Loop through the targeted items
1982                 for (var i=0; i<len; ++i) {
1983                     var dd = dds[i];
1984                     // If the cursor is over the object, it wins.  If the
1985                     // cursor is over multiple matches, the first one we come
1986                     // to wins.
1987                     if (dd.cursorIsOver) {
1988                         winner = dd;
1989                         break;
1990                     // Otherwise the object with the most overlap wins
1991                     } else {
1992                         if (!winner ||
1993                             winner.overlap.getArea() < dd.overlap.getArea()) {
1994                             winner = dd;
1995                         }
1996                     }
1997                 }
1998             }
1999
2000             return winner;
2001         },
2002
2003         /**
2004          * Refreshes the cache of the top-left and bottom-right points of the
2005          * drag and drop objects in the specified group(s).  This is in the
2006          * format that is stored in the drag and drop instance, so typical
2007          * usage is:
2008          * <code>
2009          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2010          * </code>
2011          * Alternatively:
2012          * <code>
2013          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2014          * </code>
2015          * @TODO this really should be an indexed array.  Alternatively this
2016          * method could accept both.
2017          * @method refreshCache
2018          * @param {Object} groups an associative array of groups to refresh
2019          * @static
2020          */
2021         refreshCache: function(groups) {
2022             for (var sGroup in groups) {
2023                 if ("string" != typeof sGroup) {
2024                     continue;
2025                 }
2026                 for (var i in this.ids[sGroup]) {
2027                     var oDD = this.ids[sGroup][i];
2028
2029                     if (this.isTypeOfDD(oDD)) {
2030                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2031                         var loc = this.getLocation(oDD);
2032                         if (loc) {
2033                             this.locationCache[oDD.id] = loc;
2034                         } else {
2035                             delete this.locationCache[oDD.id];
2036                             // this will unregister the drag and drop object if
2037                             // the element is not in a usable state
2038                             // oDD.unreg();
2039                         }
2040                     }
2041                 }
2042             }
2043         },
2044
2045         /**
2046          * This checks to make sure an element exists and is in the DOM.  The
2047          * main purpose is to handle cases where innerHTML is used to remove
2048          * drag and drop objects from the DOM.  IE provides an 'unspecified
2049          * error' when trying to access the offsetParent of such an element
2050          * @method verifyEl
2051          * @param {HTMLElement} el the element to check
2052          * @return {boolean} true if the element looks usable
2053          * @static
2054          */
2055         verifyEl: function(el) {
2056             if (el) {
2057                 var parent;
2058                 if(Roo.isIE){
2059                     try{
2060                         parent = el.offsetParent;
2061                     }catch(e){}
2062                 }else{
2063                     parent = el.offsetParent;
2064                 }
2065                 if (parent) {
2066                     return true;
2067                 }
2068             }
2069
2070             return false;
2071         },
2072
2073         /**
2074          * Returns a Region object containing the drag and drop element's position
2075          * and size, including the padding configured for it
2076          * @method getLocation
2077          * @param {DragDrop} oDD the drag and drop object to get the
2078          *                       location for
2079          * @return {Roo.lib.Region} a Region object representing the total area
2080          *                             the element occupies, including any padding
2081          *                             the instance is configured for.
2082          * @static
2083          */
2084         getLocation: function(oDD) {
2085             if (! this.isTypeOfDD(oDD)) {
2086                 return null;
2087             }
2088
2089             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2090
2091             try {
2092                 pos= Roo.lib.Dom.getXY(el);
2093             } catch (e) { }
2094
2095             if (!pos) {
2096                 return null;
2097             }
2098
2099             x1 = pos[0];
2100             x2 = x1 + el.offsetWidth;
2101             y1 = pos[1];
2102             y2 = y1 + el.offsetHeight;
2103
2104             t = y1 - oDD.padding[0];
2105             r = x2 + oDD.padding[1];
2106             b = y2 + oDD.padding[2];
2107             l = x1 - oDD.padding[3];
2108
2109             return new Roo.lib.Region( t, r, b, l );
2110         },
2111
2112         /**
2113          * Checks the cursor location to see if it over the target
2114          * @method isOverTarget
2115          * @param {Roo.lib.Point} pt The point to evaluate
2116          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2117          * @return {boolean} true if the mouse is over the target
2118          * @private
2119          * @static
2120          */
2121         isOverTarget: function(pt, oTarget, intersect) {
2122             // use cache if available
2123             var loc = this.locationCache[oTarget.id];
2124             if (!loc || !this.useCache) {
2125                 loc = this.getLocation(oTarget);
2126                 this.locationCache[oTarget.id] = loc;
2127
2128             }
2129
2130             if (!loc) {
2131                 return false;
2132             }
2133
2134             oTarget.cursorIsOver = loc.contains( pt );
2135
2136             // DragDrop is using this as a sanity check for the initial mousedown
2137             // in this case we are done.  In POINT mode, if the drag obj has no
2138             // contraints, we are also done. Otherwise we need to evaluate the
2139             // location of the target as related to the actual location of the
2140             // dragged element.
2141             var dc = this.dragCurrent;
2142             if (!dc || !dc.getTargetCoord ||
2143                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2144                 return oTarget.cursorIsOver;
2145             }
2146
2147             oTarget.overlap = null;
2148
2149             // Get the current location of the drag element, this is the
2150             // location of the mouse event less the delta that represents
2151             // where the original mousedown happened on the element.  We
2152             // need to consider constraints and ticks as well.
2153             var pos = dc.getTargetCoord(pt.x, pt.y);
2154
2155             var el = dc.getDragEl();
2156             var curRegion = new Roo.lib.Region( pos.y,
2157                                                    pos.x + el.offsetWidth,
2158                                                    pos.y + el.offsetHeight,
2159                                                    pos.x );
2160
2161             var overlap = curRegion.intersect(loc);
2162
2163             if (overlap) {
2164                 oTarget.overlap = overlap;
2165                 return (intersect) ? true : oTarget.cursorIsOver;
2166             } else {
2167                 return false;
2168             }
2169         },
2170
2171         /**
2172          * unload event handler
2173          * @method _onUnload
2174          * @private
2175          * @static
2176          */
2177         _onUnload: function(e, me) {
2178             Roo.dd.DragDropMgr.unregAll();
2179         },
2180
2181         /**
2182          * Cleans up the drag and drop events and objects.
2183          * @method unregAll
2184          * @private
2185          * @static
2186          */
2187         unregAll: function() {
2188
2189             if (this.dragCurrent) {
2190                 this.stopDrag();
2191                 this.dragCurrent = null;
2192             }
2193
2194             this._execOnAll("unreg", []);
2195
2196             for (i in this.elementCache) {
2197                 delete this.elementCache[i];
2198             }
2199
2200             this.elementCache = {};
2201             this.ids = {};
2202         },
2203
2204         /**
2205          * A cache of DOM elements
2206          * @property elementCache
2207          * @private
2208          * @static
2209          */
2210         elementCache: {},
2211
2212         /**
2213          * Get the wrapper for the DOM element specified
2214          * @method getElWrapper
2215          * @param {String} id the id of the element to get
2216          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2217          * @private
2218          * @deprecated This wrapper isn't that useful
2219          * @static
2220          */
2221         getElWrapper: function(id) {
2222             var oWrapper = this.elementCache[id];
2223             if (!oWrapper || !oWrapper.el) {
2224                 oWrapper = this.elementCache[id] =
2225                     new this.ElementWrapper(Roo.getDom(id));
2226             }
2227             return oWrapper;
2228         },
2229
2230         /**
2231          * Returns the actual DOM element
2232          * @method getElement
2233          * @param {String} id the id of the elment to get
2234          * @return {Object} The element
2235          * @deprecated use Roo.getDom instead
2236          * @static
2237          */
2238         getElement: function(id) {
2239             return Roo.getDom(id);
2240         },
2241
2242         /**
2243          * Returns the style property for the DOM element (i.e.,
2244          * document.getElById(id).style)
2245          * @method getCss
2246          * @param {String} id the id of the elment to get
2247          * @return {Object} The style property of the element
2248          * @deprecated use Roo.getDom instead
2249          * @static
2250          */
2251         getCss: function(id) {
2252             var el = Roo.getDom(id);
2253             return (el) ? el.style : null;
2254         },
2255
2256         /**
2257          * Inner class for cached elements
2258          * @class DragDropMgr.ElementWrapper
2259          * @for DragDropMgr
2260          * @private
2261          * @deprecated
2262          */
2263         ElementWrapper: function(el) {
2264                 /**
2265                  * The element
2266                  * @property el
2267                  */
2268                 this.el = el || null;
2269                 /**
2270                  * The element id
2271                  * @property id
2272                  */
2273                 this.id = this.el && el.id;
2274                 /**
2275                  * A reference to the style property
2276                  * @property css
2277                  */
2278                 this.css = this.el && el.style;
2279             },
2280
2281         /**
2282          * Returns the X position of an html element
2283          * @method getPosX
2284          * @param el the element for which to get the position
2285          * @return {int} the X coordinate
2286          * @for DragDropMgr
2287          * @deprecated use Roo.lib.Dom.getX instead
2288          * @static
2289          */
2290         getPosX: function(el) {
2291             return Roo.lib.Dom.getX(el);
2292         },
2293
2294         /**
2295          * Returns the Y position of an html element
2296          * @method getPosY
2297          * @param el the element for which to get the position
2298          * @return {int} the Y coordinate
2299          * @deprecated use Roo.lib.Dom.getY instead
2300          * @static
2301          */
2302         getPosY: function(el) {
2303             return Roo.lib.Dom.getY(el);
2304         },
2305
2306         /**
2307          * Swap two nodes.  In IE, we use the native method, for others we
2308          * emulate the IE behavior
2309          * @method swapNode
2310          * @param n1 the first node to swap
2311          * @param n2 the other node to swap
2312          * @static
2313          */
2314         swapNode: function(n1, n2) {
2315             if (n1.swapNode) {
2316                 n1.swapNode(n2);
2317             } else {
2318                 var p = n2.parentNode;
2319                 var s = n2.nextSibling;
2320
2321                 if (s == n1) {
2322                     p.insertBefore(n1, n2);
2323                 } else if (n2 == n1.nextSibling) {
2324                     p.insertBefore(n2, n1);
2325                 } else {
2326                     n1.parentNode.replaceChild(n2, n1);
2327                     p.insertBefore(n1, s);
2328                 }
2329             }
2330         },
2331
2332         /**
2333          * Returns the current scroll position
2334          * @method getScroll
2335          * @private
2336          * @static
2337          */
2338         getScroll: function () {
2339             var t, l, dde=document.documentElement, db=document.body;
2340             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2341                 t = dde.scrollTop;
2342                 l = dde.scrollLeft;
2343             } else if (db) {
2344                 t = db.scrollTop;
2345                 l = db.scrollLeft;
2346             } else {
2347
2348             }
2349             return { top: t, left: l };
2350         },
2351
2352         /**
2353          * Returns the specified element style property
2354          * @method getStyle
2355          * @param {HTMLElement} el          the element
2356          * @param {string}      styleProp   the style property
2357          * @return {string} The value of the style property
2358          * @deprecated use Roo.lib.Dom.getStyle
2359          * @static
2360          */
2361         getStyle: function(el, styleProp) {
2362             return Roo.fly(el).getStyle(styleProp);
2363         },
2364
2365         /**
2366          * Gets the scrollTop
2367          * @method getScrollTop
2368          * @return {int} the document's scrollTop
2369          * @static
2370          */
2371         getScrollTop: function () { return this.getScroll().top; },
2372
2373         /**
2374          * Gets the scrollLeft
2375          * @method getScrollLeft
2376          * @return {int} the document's scrollTop
2377          * @static
2378          */
2379         getScrollLeft: function () { return this.getScroll().left; },
2380
2381         /**
2382          * Sets the x/y position of an element to the location of the
2383          * target element.
2384          * @method moveToEl
2385          * @param {HTMLElement} moveEl      The element to move
2386          * @param {HTMLElement} targetEl    The position reference element
2387          * @static
2388          */
2389         moveToEl: function (moveEl, targetEl) {
2390             var aCoord = Roo.lib.Dom.getXY(targetEl);
2391             Roo.lib.Dom.setXY(moveEl, aCoord);
2392         },
2393
2394         /**
2395          * Numeric array sort function
2396          * @method numericSort
2397          * @static
2398          */
2399         numericSort: function(a, b) { return (a - b); },
2400
2401         /**
2402          * Internal counter
2403          * @property _timeoutCount
2404          * @private
2405          * @static
2406          */
2407         _timeoutCount: 0,
2408
2409         /**
2410          * Trying to make the load order less important.  Without this we get
2411          * an error if this file is loaded before the Event Utility.
2412          * @method _addListeners
2413          * @private
2414          * @static
2415          */
2416         _addListeners: function() {
2417             var DDM = Roo.dd.DDM;
2418             if ( Roo.lib.Event && document ) {
2419                 DDM._onLoad();
2420             } else {
2421                 if (DDM._timeoutCount > 2000) {
2422                 } else {
2423                     setTimeout(DDM._addListeners, 10);
2424                     if (document && document.body) {
2425                         DDM._timeoutCount += 1;
2426                     }
2427                 }
2428             }
2429         },
2430
2431         /**
2432          * Recursively searches the immediate parent and all child nodes for
2433          * the handle element in order to determine wheter or not it was
2434          * clicked.
2435          * @method handleWasClicked
2436          * @param node the html element to inspect
2437          * @static
2438          */
2439         handleWasClicked: function(node, id) {
2440             if (this.isHandle(id, node.id)) {
2441                 return true;
2442             } else {
2443                 // check to see if this is a text node child of the one we want
2444                 var p = node.parentNode;
2445
2446                 while (p) {
2447                     if (this.isHandle(id, p.id)) {
2448                         return true;
2449                     } else {
2450                         p = p.parentNode;
2451                     }
2452                 }
2453             }
2454
2455             return false;
2456         }
2457
2458     };
2459
2460 }();
2461
2462 // shorter alias, save a few bytes
2463 Roo.dd.DDM = Roo.dd.DragDropMgr;
2464 Roo.dd.DDM._addListeners();
2465
2466 }/*
2467  * Based on:
2468  * Ext JS Library 1.1.1
2469  * Copyright(c) 2006-2007, Ext JS, LLC.
2470  *
2471  * Originally Released Under LGPL - original licence link has changed is not relivant.
2472  *
2473  * Fork - LGPL
2474  * <script type="text/javascript">
2475  */
2476
2477 /**
2478  * @class Roo.dd.DD
2479  * A DragDrop implementation where the linked element follows the
2480  * mouse cursor during a drag.
2481  * @extends Roo.dd.DragDrop
2482  * @constructor
2483  * @param {String} id the id of the linked element
2484  * @param {String} sGroup the group of related DragDrop items
2485  * @param {object} config an object containing configurable attributes
2486  *                Valid properties for DD:
2487  *                    scroll
2488  */
2489 Roo.dd.DD = function(id, sGroup, config) {
2490     if (id) {
2491         this.init(id, sGroup, config);
2492     }
2493 };
2494
2495 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2496
2497     /**
2498      * When set to true, the utility automatically tries to scroll the browser
2499      * window wehn a drag and drop element is dragged near the viewport boundary.
2500      * Defaults to true.
2501      * @property scroll
2502      * @type boolean
2503      */
2504     scroll: true,
2505
2506     /**
2507      * Sets the pointer offset to the distance between the linked element's top
2508      * left corner and the location the element was clicked
2509      * @method autoOffset
2510      * @param {int} iPageX the X coordinate of the click
2511      * @param {int} iPageY the Y coordinate of the click
2512      */
2513     autoOffset: function(iPageX, iPageY) {
2514         var x = iPageX - this.startPageX;
2515         var y = iPageY - this.startPageY;
2516         this.setDelta(x, y);
2517     },
2518
2519     /**
2520      * Sets the pointer offset.  You can call this directly to force the
2521      * offset to be in a particular location (e.g., pass in 0,0 to set it
2522      * to the center of the object)
2523      * @method setDelta
2524      * @param {int} iDeltaX the distance from the left
2525      * @param {int} iDeltaY the distance from the top
2526      */
2527     setDelta: function(iDeltaX, iDeltaY) {
2528         this.deltaX = iDeltaX;
2529         this.deltaY = iDeltaY;
2530     },
2531
2532     /**
2533      * Sets the drag element to the location of the mousedown or click event,
2534      * maintaining the cursor location relative to the location on the element
2535      * that was clicked.  Override this if you want to place the element in a
2536      * location other than where the cursor is.
2537      * @method setDragElPos
2538      * @param {int} iPageX the X coordinate of the mousedown or drag event
2539      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2540      */
2541     setDragElPos: function(iPageX, iPageY) {
2542         // the first time we do this, we are going to check to make sure
2543         // the element has css positioning
2544
2545         var el = this.getDragEl();
2546         this.alignElWithMouse(el, iPageX, iPageY);
2547     },
2548
2549     /**
2550      * Sets the element to the location of the mousedown or click event,
2551      * maintaining the cursor location relative to the location on the element
2552      * that was clicked.  Override this if you want to place the element in a
2553      * location other than where the cursor is.
2554      * @method alignElWithMouse
2555      * @param {HTMLElement} el the element to move
2556      * @param {int} iPageX the X coordinate of the mousedown or drag event
2557      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2558      */
2559     alignElWithMouse: function(el, iPageX, iPageY) {
2560         var oCoord = this.getTargetCoord(iPageX, iPageY);
2561         var fly = el.dom ? el : Roo.fly(el);
2562         if (!this.deltaSetXY) {
2563             var aCoord = [oCoord.x, oCoord.y];
2564             fly.setXY(aCoord);
2565             var newLeft = fly.getLeft(true);
2566             var newTop  = fly.getTop(true);
2567             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2568         } else {
2569             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2570         }
2571
2572         this.cachePosition(oCoord.x, oCoord.y);
2573         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2574         return oCoord;
2575     },
2576
2577     /**
2578      * Saves the most recent position so that we can reset the constraints and
2579      * tick marks on-demand.  We need to know this so that we can calculate the
2580      * number of pixels the element is offset from its original position.
2581      * @method cachePosition
2582      * @param iPageX the current x position (optional, this just makes it so we
2583      * don't have to look it up again)
2584      * @param iPageY the current y position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      */
2587     cachePosition: function(iPageX, iPageY) {
2588         if (iPageX) {
2589             this.lastPageX = iPageX;
2590             this.lastPageY = iPageY;
2591         } else {
2592             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2593             this.lastPageX = aCoord[0];
2594             this.lastPageY = aCoord[1];
2595         }
2596     },
2597
2598     /**
2599      * Auto-scroll the window if the dragged object has been moved beyond the
2600      * visible window boundary.
2601      * @method autoScroll
2602      * @param {int} x the drag element's x position
2603      * @param {int} y the drag element's y position
2604      * @param {int} h the height of the drag element
2605      * @param {int} w the width of the drag element
2606      * @private
2607      */
2608     autoScroll: function(x, y, h, w) {
2609
2610         if (this.scroll) {
2611             // The client height
2612             var clientH = Roo.lib.Dom.getViewWidth();
2613
2614             // The client width
2615             var clientW = Roo.lib.Dom.getViewHeight();
2616
2617             // The amt scrolled down
2618             var st = this.DDM.getScrollTop();
2619
2620             // The amt scrolled right
2621             var sl = this.DDM.getScrollLeft();
2622
2623             // Location of the bottom of the element
2624             var bot = h + y;
2625
2626             // Location of the right of the element
2627             var right = w + x;
2628
2629             // The distance from the cursor to the bottom of the visible area,
2630             // adjusted so that we don't scroll if the cursor is beyond the
2631             // element drag constraints
2632             var toBot = (clientH + st - y - this.deltaY);
2633
2634             // The distance from the cursor to the right of the visible area
2635             var toRight = (clientW + sl - x - this.deltaX);
2636
2637
2638             // How close to the edge the cursor must be before we scroll
2639             // var thresh = (document.all) ? 100 : 40;
2640             var thresh = 40;
2641
2642             // How many pixels to scroll per autoscroll op.  This helps to reduce
2643             // clunky scrolling. IE is more sensitive about this ... it needs this
2644             // value to be higher.
2645             var scrAmt = (document.all) ? 80 : 30;
2646
2647             // Scroll down if we are near the bottom of the visible page and the
2648             // obj extends below the crease
2649             if ( bot > clientH && toBot < thresh ) {
2650                 window.scrollTo(sl, st + scrAmt);
2651             }
2652
2653             // Scroll up if the window is scrolled down and the top of the object
2654             // goes above the top border
2655             if ( y < st && st > 0 && y - st < thresh ) {
2656                 window.scrollTo(sl, st - scrAmt);
2657             }
2658
2659             // Scroll right if the obj is beyond the right border and the cursor is
2660             // near the border.
2661             if ( right > clientW && toRight < thresh ) {
2662                 window.scrollTo(sl + scrAmt, st);
2663             }
2664
2665             // Scroll left if the window has been scrolled to the right and the obj
2666             // extends past the left border
2667             if ( x < sl && sl > 0 && x - sl < thresh ) {
2668                 window.scrollTo(sl - scrAmt, st);
2669             }
2670         }
2671     },
2672
2673     /**
2674      * Finds the location the element should be placed if we want to move
2675      * it to where the mouse location less the click offset would place us.
2676      * @method getTargetCoord
2677      * @param {int} iPageX the X coordinate of the click
2678      * @param {int} iPageY the Y coordinate of the click
2679      * @return an object that contains the coordinates (Object.x and Object.y)
2680      * @private
2681      */
2682     getTargetCoord: function(iPageX, iPageY) {
2683
2684
2685         var x = iPageX - this.deltaX;
2686         var y = iPageY - this.deltaY;
2687
2688         if (this.constrainX) {
2689             if (x < this.minX) { x = this.minX; }
2690             if (x > this.maxX) { x = this.maxX; }
2691         }
2692
2693         if (this.constrainY) {
2694             if (y < this.minY) { y = this.minY; }
2695             if (y > this.maxY) { y = this.maxY; }
2696         }
2697
2698         x = this.getTick(x, this.xTicks);
2699         y = this.getTick(y, this.yTicks);
2700
2701
2702         return {x:x, y:y};
2703     },
2704
2705     /*
2706      * Sets up config options specific to this class. Overrides
2707      * Roo.dd.DragDrop, but all versions of this method through the
2708      * inheritance chain are called
2709      */
2710     applyConfig: function() {
2711         Roo.dd.DD.superclass.applyConfig.call(this);
2712         this.scroll = (this.config.scroll !== false);
2713     },
2714
2715     /*
2716      * Event that fires prior to the onMouseDown event.  Overrides
2717      * Roo.dd.DragDrop.
2718      */
2719     b4MouseDown: function(e) {
2720         // this.resetConstraints();
2721         this.autoOffset(e.getPageX(),
2722                             e.getPageY());
2723     },
2724
2725     /*
2726      * Event that fires prior to the onDrag event.  Overrides
2727      * Roo.dd.DragDrop.
2728      */
2729     b4Drag: function(e) {
2730         this.setDragElPos(e.getPageX(),
2731                             e.getPageY());
2732     },
2733
2734     toString: function() {
2735         return ("DD " + this.id);
2736     }
2737
2738     //////////////////////////////////////////////////////////////////////////
2739     // Debugging ygDragDrop events that can be overridden
2740     //////////////////////////////////////////////////////////////////////////
2741     /*
2742     startDrag: function(x, y) {
2743     },
2744
2745     onDrag: function(e) {
2746     },
2747
2748     onDragEnter: function(e, id) {
2749     },
2750
2751     onDragOver: function(e, id) {
2752     },
2753
2754     onDragOut: function(e, id) {
2755     },
2756
2757     onDragDrop: function(e, id) {
2758     },
2759
2760     endDrag: function(e) {
2761     }
2762
2763     */
2764
2765 });/*
2766  * Based on:
2767  * Ext JS Library 1.1.1
2768  * Copyright(c) 2006-2007, Ext JS, LLC.
2769  *
2770  * Originally Released Under LGPL - original licence link has changed is not relivant.
2771  *
2772  * Fork - LGPL
2773  * <script type="text/javascript">
2774  */
2775
2776 /**
2777  * @class Roo.dd.DDProxy
2778  * A DragDrop implementation that inserts an empty, bordered div into
2779  * the document that follows the cursor during drag operations.  At the time of
2780  * the click, the frame div is resized to the dimensions of the linked html
2781  * element, and moved to the exact location of the linked element.
2782  *
2783  * References to the "frame" element refer to the single proxy element that
2784  * was created to be dragged in place of all DDProxy elements on the
2785  * page.
2786  *
2787  * @extends Roo.dd.DD
2788  * @constructor
2789  * @param {String} id the id of the linked html element
2790  * @param {String} sGroup the group of related DragDrop objects
2791  * @param {object} config an object containing configurable attributes
2792  *                Valid properties for DDProxy in addition to those in DragDrop:
2793  *                   resizeFrame, centerFrame, dragElId
2794  */
2795 Roo.dd.DDProxy = function(id, sGroup, config) {
2796     if (id) {
2797         this.init(id, sGroup, config);
2798         this.initFrame();
2799     }
2800 };
2801
2802 /**
2803  * The default drag frame div id
2804  * @property Roo.dd.DDProxy.dragElId
2805  * @type String
2806  * @static
2807  */
2808 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2809
2810 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2811
2812     /**
2813      * By default we resize the drag frame to be the same size as the element
2814      * we want to drag (this is to get the frame effect).  We can turn it off
2815      * if we want a different behavior.
2816      * @property resizeFrame
2817      * @type boolean
2818      */
2819     resizeFrame: true,
2820
2821     /**
2822      * By default the frame is positioned exactly where the drag element is, so
2823      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2824      * you do not have constraints on the obj is to have the drag frame centered
2825      * around the cursor.  Set centerFrame to true for this effect.
2826      * @property centerFrame
2827      * @type boolean
2828      */
2829     centerFrame: false,
2830
2831     /**
2832      * Creates the proxy element if it does not yet exist
2833      * @method createFrame
2834      */
2835     createFrame: function() {
2836         var self = this;
2837         var body = document.body;
2838
2839         if (!body || !body.firstChild) {
2840             setTimeout( function() { self.createFrame(); }, 50 );
2841             return;
2842         }
2843
2844         var div = this.getDragEl();
2845
2846         if (!div) {
2847             div    = document.createElement("div");
2848             div.id = this.dragElId;
2849             var s  = div.style;
2850
2851             s.position   = "absolute";
2852             s.visibility = "hidden";
2853             s.cursor     = "move";
2854             s.border     = "2px solid #aaa";
2855             s.zIndex     = 999;
2856
2857             // appendChild can blow up IE if invoked prior to the window load event
2858             // while rendering a table.  It is possible there are other scenarios
2859             // that would cause this to happen as well.
2860             body.insertBefore(div, body.firstChild);
2861         }
2862     },
2863
2864     /**
2865      * Initialization for the drag frame element.  Must be called in the
2866      * constructor of all subclasses
2867      * @method initFrame
2868      */
2869     initFrame: function() {
2870         this.createFrame();
2871     },
2872
2873     applyConfig: function() {
2874         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2875
2876         this.resizeFrame = (this.config.resizeFrame !== false);
2877         this.centerFrame = (this.config.centerFrame);
2878         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2879     },
2880
2881     /**
2882      * Resizes the drag frame to the dimensions of the clicked object, positions
2883      * it over the object, and finally displays it
2884      * @method showFrame
2885      * @param {int} iPageX X click position
2886      * @param {int} iPageY Y click position
2887      * @private
2888      */
2889     showFrame: function(iPageX, iPageY) {
2890         var el = this.getEl();
2891         var dragEl = this.getDragEl();
2892         var s = dragEl.style;
2893
2894         this._resizeProxy();
2895
2896         if (this.centerFrame) {
2897             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2898                            Math.round(parseInt(s.height, 10)/2) );
2899         }
2900
2901         this.setDragElPos(iPageX, iPageY);
2902
2903         Roo.fly(dragEl).show();
2904     },
2905
2906     /**
2907      * The proxy is automatically resized to the dimensions of the linked
2908      * element when a drag is initiated, unless resizeFrame is set to false
2909      * @method _resizeProxy
2910      * @private
2911      */
2912     _resizeProxy: function() {
2913         if (this.resizeFrame) {
2914             var el = this.getEl();
2915             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2916         }
2917     },
2918
2919     // overrides Roo.dd.DragDrop
2920     b4MouseDown: function(e) {
2921         var x = e.getPageX();
2922         var y = e.getPageY();
2923         this.autoOffset(x, y);
2924         this.setDragElPos(x, y);
2925     },
2926
2927     // overrides Roo.dd.DragDrop
2928     b4StartDrag: function(x, y) {
2929         // show the drag frame
2930         this.showFrame(x, y);
2931     },
2932
2933     // overrides Roo.dd.DragDrop
2934     b4EndDrag: function(e) {
2935         Roo.fly(this.getDragEl()).hide();
2936     },
2937
2938     // overrides Roo.dd.DragDrop
2939     // By default we try to move the element to the last location of the frame.
2940     // This is so that the default behavior mirrors that of Roo.dd.DD.
2941     endDrag: function(e) {
2942
2943         var lel = this.getEl();
2944         var del = this.getDragEl();
2945
2946         // Show the drag frame briefly so we can get its position
2947         del.style.visibility = "";
2948
2949         this.beforeMove();
2950         // Hide the linked element before the move to get around a Safari
2951         // rendering bug.
2952         lel.style.visibility = "hidden";
2953         Roo.dd.DDM.moveToEl(lel, del);
2954         del.style.visibility = "hidden";
2955         lel.style.visibility = "";
2956
2957         this.afterDrag();
2958     },
2959
2960     beforeMove : function(){
2961
2962     },
2963
2964     afterDrag : function(){
2965
2966     },
2967
2968     toString: function() {
2969         return ("DDProxy " + this.id);
2970     }
2971
2972 });
2973 /*
2974  * Based on:
2975  * Ext JS Library 1.1.1
2976  * Copyright(c) 2006-2007, Ext JS, LLC.
2977  *
2978  * Originally Released Under LGPL - original licence link has changed is not relivant.
2979  *
2980  * Fork - LGPL
2981  * <script type="text/javascript">
2982  */
2983
2984  /**
2985  * @class Roo.dd.DDTarget
2986  * A DragDrop implementation that does not move, but can be a drop
2987  * target.  You would get the same result by simply omitting implementation
2988  * for the event callbacks, but this way we reduce the processing cost of the
2989  * event listener and the callbacks.
2990  * @extends Roo.dd.DragDrop
2991  * @constructor
2992  * @param {String} id the id of the element that is a drop target
2993  * @param {String} sGroup the group of related DragDrop objects
2994  * @param {object} config an object containing configurable attributes
2995  *                 Valid properties for DDTarget in addition to those in
2996  *                 DragDrop:
2997  *                    none
2998  */
2999 Roo.dd.DDTarget = function(id, sGroup, config) {
3000     if (id) {
3001         this.initTarget(id, sGroup, config);
3002     }
3003 };
3004
3005 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3006 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3007     toString: function() {
3008         return ("DDTarget " + this.id);
3009     }
3010 });
3011 /*
3012  * Based on:
3013  * Ext JS Library 1.1.1
3014  * Copyright(c) 2006-2007, Ext JS, LLC.
3015  *
3016  * Originally Released Under LGPL - original licence link has changed is not relivant.
3017  *
3018  * Fork - LGPL
3019  * <script type="text/javascript">
3020  */
3021  
3022
3023 /**
3024  * @class Roo.dd.ScrollManager
3025  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3026  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3027  * @singleton
3028  */
3029 Roo.dd.ScrollManager = function(){
3030     var ddm = Roo.dd.DragDropMgr;
3031     var els = {};
3032     var dragEl = null;
3033     var proc = {};
3034     
3035     var onStop = function(e){
3036         dragEl = null;
3037         clearProc();
3038     };
3039     
3040     var triggerRefresh = function(){
3041         if(ddm.dragCurrent){
3042              ddm.refreshCache(ddm.dragCurrent.groups);
3043         }
3044     };
3045     
3046     var doScroll = function(){
3047         if(ddm.dragCurrent){
3048             var dds = Roo.dd.ScrollManager;
3049             if(!dds.animate){
3050                 if(proc.el.scroll(proc.dir, dds.increment)){
3051                     triggerRefresh();
3052                 }
3053             }else{
3054                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3055             }
3056         }
3057     };
3058     
3059     var clearProc = function(){
3060         if(proc.id){
3061             clearInterval(proc.id);
3062         }
3063         proc.id = 0;
3064         proc.el = null;
3065         proc.dir = "";
3066     };
3067     
3068     var startProc = function(el, dir){
3069         clearProc();
3070         proc.el = el;
3071         proc.dir = dir;
3072         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3073     };
3074     
3075     var onFire = function(e, isDrop){
3076         if(isDrop || !ddm.dragCurrent){ return; }
3077         var dds = Roo.dd.ScrollManager;
3078         if(!dragEl || dragEl != ddm.dragCurrent){
3079             dragEl = ddm.dragCurrent;
3080             // refresh regions on drag start
3081             dds.refreshCache();
3082         }
3083         
3084         var xy = Roo.lib.Event.getXY(e);
3085         var pt = new Roo.lib.Point(xy[0], xy[1]);
3086         for(var id in els){
3087             var el = els[id], r = el._region;
3088             if(r && r.contains(pt) && el.isScrollable()){
3089                 if(r.bottom - pt.y <= dds.thresh){
3090                     if(proc.el != el){
3091                         startProc(el, "down");
3092                     }
3093                     return;
3094                 }else if(r.right - pt.x <= dds.thresh){
3095                     if(proc.el != el){
3096                         startProc(el, "left");
3097                     }
3098                     return;
3099                 }else if(pt.y - r.top <= dds.thresh){
3100                     if(proc.el != el){
3101                         startProc(el, "up");
3102                     }
3103                     return;
3104                 }else if(pt.x - r.left <= dds.thresh){
3105                     if(proc.el != el){
3106                         startProc(el, "right");
3107                     }
3108                     return;
3109                 }
3110             }
3111         }
3112         clearProc();
3113     };
3114     
3115     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3116     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3117     
3118     return {
3119         /**
3120          * Registers new overflow element(s) to auto scroll
3121          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3122          */
3123         register : function(el){
3124             if(el instanceof Array){
3125                 for(var i = 0, len = el.length; i < len; i++) {
3126                         this.register(el[i]);
3127                 }
3128             }else{
3129                 el = Roo.get(el);
3130                 els[el.id] = el;
3131             }
3132         },
3133         
3134         /**
3135          * Unregisters overflow element(s) so they are no longer scrolled
3136          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3137          */
3138         unregister : function(el){
3139             if(el instanceof Array){
3140                 for(var i = 0, len = el.length; i < len; i++) {
3141                         this.unregister(el[i]);
3142                 }
3143             }else{
3144                 el = Roo.get(el);
3145                 delete els[el.id];
3146             }
3147         },
3148         
3149         /**
3150          * The number of pixels from the edge of a container the pointer needs to be to 
3151          * trigger scrolling (defaults to 25)
3152          * @type Number
3153          */
3154         thresh : 25,
3155         
3156         /**
3157          * The number of pixels to scroll in each scroll increment (defaults to 50)
3158          * @type Number
3159          */
3160         increment : 100,
3161         
3162         /**
3163          * The frequency of scrolls in milliseconds (defaults to 500)
3164          * @type Number
3165          */
3166         frequency : 500,
3167         
3168         /**
3169          * True to animate the scroll (defaults to true)
3170          * @type Boolean
3171          */
3172         animate: true,
3173         
3174         /**
3175          * The animation duration in seconds - 
3176          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3177          * @type Number
3178          */
3179         animDuration: .4,
3180         
3181         /**
3182          * Manually trigger a cache refresh.
3183          */
3184         refreshCache : function(){
3185             for(var id in els){
3186                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3187                     els[id]._region = els[id].getRegion();
3188                 }
3189             }
3190         }
3191     };
3192 }();/*
3193  * Based on:
3194  * Ext JS Library 1.1.1
3195  * Copyright(c) 2006-2007, Ext JS, LLC.
3196  *
3197  * Originally Released Under LGPL - original licence link has changed is not relivant.
3198  *
3199  * Fork - LGPL
3200  * <script type="text/javascript">
3201  */
3202  
3203
3204 /**
3205  * @class Roo.dd.Registry
3206  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3207  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3208  * @singleton
3209  */
3210 Roo.dd.Registry = function(){
3211     var elements = {}; 
3212     var handles = {}; 
3213     var autoIdSeed = 0;
3214
3215     var getId = function(el, autogen){
3216         if(typeof el == "string"){
3217             return el;
3218         }
3219         var id = el.id;
3220         if(!id && autogen !== false){
3221             id = "roodd-" + (++autoIdSeed);
3222             el.id = id;
3223         }
3224         return id;
3225     };
3226     
3227     return {
3228     /**
3229      * Register a drag drop element
3230      * @param {String|HTMLElement} element The id or DOM node to register
3231      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3232      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3233      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3234      * populated in the data object (if applicable):
3235      * <pre>
3236 Value      Description<br />
3237 ---------  ------------------------------------------<br />
3238 handles    Array of DOM nodes that trigger dragging<br />
3239            for the element being registered<br />
3240 isHandle   True if the element passed in triggers<br />
3241            dragging itself, else false
3242 </pre>
3243      */
3244         register : function(el, data){
3245             data = data || {};
3246             if(typeof el == "string"){
3247                 el = document.getElementById(el);
3248             }
3249             data.ddel = el;
3250             elements[getId(el)] = data;
3251             if(data.isHandle !== false){
3252                 handles[data.ddel.id] = data;
3253             }
3254             if(data.handles){
3255                 var hs = data.handles;
3256                 for(var i = 0, len = hs.length; i < len; i++){
3257                         handles[getId(hs[i])] = data;
3258                 }
3259             }
3260         },
3261
3262     /**
3263      * Unregister a drag drop element
3264      * @param {String|HTMLElement}  element The id or DOM node to unregister
3265      */
3266         unregister : function(el){
3267             var id = getId(el, false);
3268             var data = elements[id];
3269             if(data){
3270                 delete elements[id];
3271                 if(data.handles){
3272                     var hs = data.handles;
3273                     for(var i = 0, len = hs.length; i < len; i++){
3274                         delete handles[getId(hs[i], false)];
3275                     }
3276                 }
3277             }
3278         },
3279
3280     /**
3281      * Returns the handle registered for a DOM Node by id
3282      * @param {String|HTMLElement} id The DOM node or id to look up
3283      * @return {Object} handle The custom handle data
3284      */
3285         getHandle : function(id){
3286             if(typeof id != "string"){ // must be element?
3287                 id = id.id;
3288             }
3289             return handles[id];
3290         },
3291
3292     /**
3293      * Returns the handle that is registered for the DOM node that is the target of the event
3294      * @param {Event} e The event
3295      * @return {Object} handle The custom handle data
3296      */
3297         getHandleFromEvent : function(e){
3298             var t = Roo.lib.Event.getTarget(e);
3299             return t ? handles[t.id] : null;
3300         },
3301
3302     /**
3303      * Returns a custom data object that is registered for a DOM node by id
3304      * @param {String|HTMLElement} id The DOM node or id to look up
3305      * @return {Object} data The custom data
3306      */
3307         getTarget : function(id){
3308             if(typeof id != "string"){ // must be element?
3309                 id = id.id;
3310             }
3311             return elements[id];
3312         },
3313
3314     /**
3315      * Returns a custom data object that is registered for the DOM node that is the target of the event
3316      * @param {Event} e The event
3317      * @return {Object} data The custom data
3318      */
3319         getTargetFromEvent : function(e){
3320             var t = Roo.lib.Event.getTarget(e);
3321             return t ? elements[t.id] || handles[t.id] : null;
3322         }
3323     };
3324 }();/*
3325  * Based on:
3326  * Ext JS Library 1.1.1
3327  * Copyright(c) 2006-2007, Ext JS, LLC.
3328  *
3329  * Originally Released Under LGPL - original licence link has changed is not relivant.
3330  *
3331  * Fork - LGPL
3332  * <script type="text/javascript">
3333  */
3334  
3335
3336 /**
3337  * @class Roo.dd.StatusProxy
3338  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3339  * default drag proxy used by all Roo.dd components.
3340  * @constructor
3341  * @param {Object} config
3342  */
3343 Roo.dd.StatusProxy = function(config){
3344     Roo.apply(this, config);
3345     this.id = this.id || Roo.id();
3346     this.el = new Roo.Layer({
3347         dh: {
3348             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3349                 {tag: "div", cls: "x-dd-drop-icon"},
3350                 {tag: "div", cls: "x-dd-drag-ghost"}
3351             ]
3352         }, 
3353         shadow: !config || config.shadow !== false
3354     });
3355     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3356     this.dropStatus = this.dropNotAllowed;
3357 };
3358
3359 Roo.dd.StatusProxy.prototype = {
3360     /**
3361      * @cfg {String} dropAllowed
3362      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3363      */
3364     dropAllowed : "x-dd-drop-ok",
3365     /**
3366      * @cfg {String} dropNotAllowed
3367      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3368      */
3369     dropNotAllowed : "x-dd-drop-nodrop",
3370
3371     /**
3372      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3373      * over the current target element.
3374      * @param {String} cssClass The css class for the new drop status indicator image
3375      */
3376     setStatus : function(cssClass){
3377         cssClass = cssClass || this.dropNotAllowed;
3378         if(this.dropStatus != cssClass){
3379             this.el.replaceClass(this.dropStatus, cssClass);
3380             this.dropStatus = cssClass;
3381         }
3382     },
3383
3384     /**
3385      * Resets the status indicator to the default dropNotAllowed value
3386      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3387      */
3388     reset : function(clearGhost){
3389         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3390         this.dropStatus = this.dropNotAllowed;
3391         if(clearGhost){
3392             this.ghost.update("");
3393         }
3394     },
3395
3396     /**
3397      * Updates the contents of the ghost element
3398      * @param {String} html The html that will replace the current innerHTML of the ghost element
3399      */
3400     update : function(html){
3401         if(typeof html == "string"){
3402             this.ghost.update(html);
3403         }else{
3404             this.ghost.update("");
3405             html.style.margin = "0";
3406             this.ghost.dom.appendChild(html);
3407         }
3408         // ensure float = none set?? cant remember why though.
3409         var el = this.ghost.dom.firstChild;
3410                 if(el){
3411                         Roo.fly(el).setStyle('float', 'none');
3412                 }
3413     },
3414     
3415     /**
3416      * Returns the underlying proxy {@link Roo.Layer}
3417      * @return {Roo.Layer} el
3418     */
3419     getEl : function(){
3420         return this.el;
3421     },
3422
3423     /**
3424      * Returns the ghost element
3425      * @return {Roo.Element} el
3426      */
3427     getGhost : function(){
3428         return this.ghost;
3429     },
3430
3431     /**
3432      * Hides the proxy
3433      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3434      */
3435     hide : function(clear){
3436         this.el.hide();
3437         if(clear){
3438             this.reset(true);
3439         }
3440     },
3441
3442     /**
3443      * Stops the repair animation if it's currently running
3444      */
3445     stop : function(){
3446         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3447             this.anim.stop();
3448         }
3449     },
3450
3451     /**
3452      * Displays this proxy
3453      */
3454     show : function(){
3455         this.el.show();
3456     },
3457
3458     /**
3459      * Force the Layer to sync its shadow and shim positions to the element
3460      */
3461     sync : function(){
3462         this.el.sync();
3463     },
3464
3465     /**
3466      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3467      * invalid drop operation by the item being dragged.
3468      * @param {Array} xy The XY position of the element ([x, y])
3469      * @param {Function} callback The function to call after the repair is complete
3470      * @param {Object} scope The scope in which to execute the callback
3471      */
3472     repair : function(xy, callback, scope){
3473         this.callback = callback;
3474         this.scope = scope;
3475         if(xy && this.animRepair !== false){
3476             this.el.addClass("x-dd-drag-repair");
3477             this.el.hideUnders(true);
3478             this.anim = this.el.shift({
3479                 duration: this.repairDuration || .5,
3480                 easing: 'easeOut',
3481                 xy: xy,
3482                 stopFx: true,
3483                 callback: this.afterRepair,
3484                 scope: this
3485             });
3486         }else{
3487             this.afterRepair();
3488         }
3489     },
3490
3491     // private
3492     afterRepair : function(){
3493         this.hide(true);
3494         if(typeof this.callback == "function"){
3495             this.callback.call(this.scope || this);
3496         }
3497         this.callback = null;
3498         this.scope = null;
3499     }
3500 };/*
3501  * Based on:
3502  * Ext JS Library 1.1.1
3503  * Copyright(c) 2006-2007, Ext JS, LLC.
3504  *
3505  * Originally Released Under LGPL - original licence link has changed is not relivant.
3506  *
3507  * Fork - LGPL
3508  * <script type="text/javascript">
3509  */
3510
3511 /**
3512  * @class Roo.dd.DragSource
3513  * @extends Roo.dd.DDProxy
3514  * A simple class that provides the basic implementation needed to make any element draggable.
3515  * @constructor
3516  * @param {String/HTMLElement/Element} el The container element
3517  * @param {Object} config
3518  */
3519 Roo.dd.DragSource = function(el, config){
3520     this.el = Roo.get(el);
3521     this.dragData = {};
3522     
3523     Roo.apply(this, config);
3524     
3525     if(!this.proxy){
3526         this.proxy = new Roo.dd.StatusProxy();
3527     }
3528
3529     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3530           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3531     
3532     this.dragging = false;
3533 };
3534
3535 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3536     /**
3537      * @cfg {String} dropAllowed
3538      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3539      */
3540     dropAllowed : "x-dd-drop-ok",
3541     /**
3542      * @cfg {String} dropNotAllowed
3543      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3544      */
3545     dropNotAllowed : "x-dd-drop-nodrop",
3546
3547     /**
3548      * Returns the data object associated with this drag source
3549      * @return {Object} data An object containing arbitrary data
3550      */
3551     getDragData : function(e){
3552         return this.dragData;
3553     },
3554
3555     // private
3556     onDragEnter : function(e, id){
3557         var target = Roo.dd.DragDropMgr.getDDById(id);
3558         this.cachedTarget = target;
3559         if(this.beforeDragEnter(target, e, id) !== false){
3560             if(target.isNotifyTarget){
3561                 var status = target.notifyEnter(this, e, this.dragData);
3562                 this.proxy.setStatus(status);
3563             }else{
3564                 this.proxy.setStatus(this.dropAllowed);
3565             }
3566             
3567             if(this.afterDragEnter){
3568                 /**
3569                  * An empty function by default, but provided so that you can perform a custom action
3570                  * when the dragged item enters the drop target by providing an implementation.
3571                  * @param {Roo.dd.DragDrop} target The drop target
3572                  * @param {Event} e The event object
3573                  * @param {String} id The id of the dragged element
3574                  * @method afterDragEnter
3575                  */
3576                 this.afterDragEnter(target, e, id);
3577             }
3578         }
3579     },
3580
3581     /**
3582      * An empty function by default, but provided so that you can perform a custom action
3583      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
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      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3588      */
3589     beforeDragEnter : function(target, e, id){
3590         return true;
3591     },
3592
3593     // private
3594     alignElWithMouse: function() {
3595         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3596         this.proxy.sync();
3597     },
3598
3599     // private
3600     onDragOver : function(e, id){
3601         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3602         if(this.beforeDragOver(target, e, id) !== false){
3603             if(target.isNotifyTarget){
3604                 var status = target.notifyOver(this, e, this.dragData);
3605                 this.proxy.setStatus(status);
3606             }
3607
3608             if(this.afterDragOver){
3609                 /**
3610                  * An empty function by default, but provided so that you can perform a custom action
3611                  * while the dragged item is over the drop target by providing an implementation.
3612                  * @param {Roo.dd.DragDrop} target The drop target
3613                  * @param {Event} e The event object
3614                  * @param {String} id The id of the dragged element
3615                  * @method afterDragOver
3616                  */
3617                 this.afterDragOver(target, e, id);
3618             }
3619         }
3620     },
3621
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 and optionally cancel the onDragOver.
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      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3629      */
3630     beforeDragOver : function(target, e, id){
3631         return true;
3632     },
3633
3634     // private
3635     onDragOut : function(e, id){
3636         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3637         if(this.beforeDragOut(target, e, id) !== false){
3638             if(target.isNotifyTarget){
3639                 target.notifyOut(this, e, this.dragData);
3640             }
3641             this.proxy.reset();
3642             if(this.afterDragOut){
3643                 /**
3644                  * An empty function by default, but provided so that you can perform a custom action
3645                  * after the dragged item is dragged out of the target without dropping.
3646                  * @param {Roo.dd.DragDrop} target The drop target
3647                  * @param {Event} e The event object
3648                  * @param {String} id The id of the dragged element
3649                  * @method afterDragOut
3650                  */
3651                 this.afterDragOut(target, e, id);
3652             }
3653         }
3654         this.cachedTarget = null;
3655     },
3656
3657     /**
3658      * An empty function by default, but provided so that you can perform a custom action before the dragged
3659      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3660      * @param {Roo.dd.DragDrop} target The drop target
3661      * @param {Event} e The event object
3662      * @param {String} id The id of the dragged element
3663      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3664      */
3665     beforeDragOut : function(target, e, id){
3666         return true;
3667     },
3668     
3669     // private
3670     onDragDrop : function(e, id){
3671         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3672         if(this.beforeDragDrop(target, e, id) !== false){
3673             if(target.isNotifyTarget){
3674                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3675                     this.onValidDrop(target, e, id);
3676                 }else{
3677                     this.onInvalidDrop(target, e, id);
3678                 }
3679             }else{
3680                 this.onValidDrop(target, e, id);
3681             }
3682             
3683             if(this.afterDragDrop){
3684                 /**
3685                  * An empty function by default, but provided so that you can perform a custom action
3686                  * after a valid drag drop has occurred by providing an implementation.
3687                  * @param {Roo.dd.DragDrop} target The drop target
3688                  * @param {Event} e The event object
3689                  * @param {String} id The id of the dropped element
3690                  * @method afterDragDrop
3691                  */
3692                 this.afterDragDrop(target, e, id);
3693             }
3694         }
3695         delete this.cachedTarget;
3696     },
3697
3698     /**
3699      * An empty function by default, but provided so that you can perform a custom action before the dragged
3700      * item is dropped onto the target and optionally cancel the onDragDrop.
3701      * @param {Roo.dd.DragDrop} target The drop target
3702      * @param {Event} e The event object
3703      * @param {String} id The id of the dragged element
3704      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3705      */
3706     beforeDragDrop : function(target, e, id){
3707         return true;
3708     },
3709
3710     // private
3711     onValidDrop : function(target, e, id){
3712         this.hideProxy();
3713         if(this.afterValidDrop){
3714             /**
3715              * An empty function by default, but provided so that you can perform a custom action
3716              * after a valid drop has occurred by providing an implementation.
3717              * @param {Object} target The target DD 
3718              * @param {Event} e The event object
3719              * @param {String} id The id of the dropped element
3720              * @method afterInvalidDrop
3721              */
3722             this.afterValidDrop(target, e, id);
3723         }
3724     },
3725
3726     // private
3727     getRepairXY : function(e, data){
3728         return this.el.getXY();  
3729     },
3730
3731     // private
3732     onInvalidDrop : function(target, e, id){
3733         this.beforeInvalidDrop(target, e, id);
3734         if(this.cachedTarget){
3735             if(this.cachedTarget.isNotifyTarget){
3736                 this.cachedTarget.notifyOut(this, e, this.dragData);
3737             }
3738             this.cacheTarget = null;
3739         }
3740         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3741
3742         if(this.afterInvalidDrop){
3743             /**
3744              * An empty function by default, but provided so that you can perform a custom action
3745              * after an invalid drop has occurred by providing an implementation.
3746              * @param {Event} e The event object
3747              * @param {String} id The id of the dropped element
3748              * @method afterInvalidDrop
3749              */
3750             this.afterInvalidDrop(e, id);
3751         }
3752     },
3753
3754     // private
3755     afterRepair : function(){
3756         if(Roo.enableFx){
3757             this.el.highlight(this.hlColor || "c3daf9");
3758         }
3759         this.dragging = false;
3760     },
3761
3762     /**
3763      * An empty function by default, but provided so that you can perform a custom action after an invalid
3764      * drop has occurred.
3765      * @param {Roo.dd.DragDrop} target The drop target
3766      * @param {Event} e The event object
3767      * @param {String} id The id of the dragged element
3768      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3769      */
3770     beforeInvalidDrop : function(target, e, id){
3771         return true;
3772     },
3773
3774     // private
3775     handleMouseDown : function(e){
3776         if(this.dragging) {
3777             return;
3778         }
3779         var data = this.getDragData(e);
3780         if(data && this.onBeforeDrag(data, e) !== false){
3781             this.dragData = data;
3782             this.proxy.stop();
3783             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3784         } 
3785     },
3786
3787     /**
3788      * An empty function by default, but provided so that you can perform a custom action before the initial
3789      * drag event begins and optionally cancel it.
3790      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3791      * @param {Event} e The event object
3792      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3793      */
3794     onBeforeDrag : function(data, e){
3795         return true;
3796     },
3797
3798     /**
3799      * An empty function by default, but provided so that you can perform a custom action once the initial
3800      * drag event has begun.  The drag cannot be canceled from this function.
3801      * @param {Number} x The x position of the click on the dragged object
3802      * @param {Number} y The y position of the click on the dragged object
3803      */
3804     onStartDrag : Roo.emptyFn,
3805
3806     // private - YUI override
3807     startDrag : function(x, y){
3808         this.proxy.reset();
3809         this.dragging = true;
3810         this.proxy.update("");
3811         this.onInitDrag(x, y);
3812         this.proxy.show();
3813     },
3814
3815     // private
3816     onInitDrag : function(x, y){
3817         var clone = this.el.dom.cloneNode(true);
3818         clone.id = Roo.id(); // prevent duplicate ids
3819         this.proxy.update(clone);
3820         this.onStartDrag(x, y);
3821         return true;
3822     },
3823
3824     /**
3825      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3826      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3827      */
3828     getProxy : function(){
3829         return this.proxy;  
3830     },
3831
3832     /**
3833      * Hides the drag source's {@link Roo.dd.StatusProxy}
3834      */
3835     hideProxy : function(){
3836         this.proxy.hide();  
3837         this.proxy.reset(true);
3838         this.dragging = false;
3839     },
3840
3841     // private
3842     triggerCacheRefresh : function(){
3843         Roo.dd.DDM.refreshCache(this.groups);
3844     },
3845
3846     // private - override to prevent hiding
3847     b4EndDrag: function(e) {
3848     },
3849
3850     // private - override to prevent moving
3851     endDrag : function(e){
3852         this.onEndDrag(this.dragData, e);
3853     },
3854
3855     // private
3856     onEndDrag : function(data, e){
3857     },
3858     
3859     // private - pin to cursor
3860     autoOffset : function(x, y) {
3861         this.setDelta(-12, -20);
3862     }    
3863 });/*
3864  * Based on:
3865  * Ext JS Library 1.1.1
3866  * Copyright(c) 2006-2007, Ext JS, LLC.
3867  *
3868  * Originally Released Under LGPL - original licence link has changed is not relivant.
3869  *
3870  * Fork - LGPL
3871  * <script type="text/javascript">
3872  */
3873
3874
3875 /**
3876  * @class Roo.dd.DropTarget
3877  * @extends Roo.dd.DDTarget
3878  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3879  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3880  * @constructor
3881  * @param {String/HTMLElement/Element} el The container element
3882  * @param {Object} config
3883  */
3884 Roo.dd.DropTarget = function(el, config){
3885     this.el = Roo.get(el);
3886     
3887     Roo.apply(this, config);
3888     
3889     if(this.containerScroll){
3890         Roo.dd.ScrollManager.register(this.el);
3891     }
3892     
3893     Roo.dd.DropTarget.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, 
3894           {isTarget: true});
3895
3896 };
3897
3898 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3899     /**
3900      * @cfg {String} overClass
3901      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3902      */
3903     /**
3904      * @cfg {String} dropAllowed
3905      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3906      */
3907     dropAllowed : "x-dd-drop-ok",
3908     /**
3909      * @cfg {String} dropNotAllowed
3910      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3911      */
3912     dropNotAllowed : "x-dd-drop-nodrop",
3913
3914     // private
3915     isTarget : true,
3916
3917     // private
3918     isNotifyTarget : true,
3919
3920     /**
3921      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3922      * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3923      * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3924      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3925      * @param {Event} e The event
3926      * @param {Object} data An object containing arbitrary data supplied by the drag source
3927      * @return {String} status The CSS class that communicates the drop status back to the source so that the
3928      * underlying {@link Roo.dd.StatusProxy} can be updated
3929      */
3930     notifyEnter : function(dd, e, data){
3931         if(this.overClass){
3932             this.el.addClass(this.overClass);
3933         }
3934         return this.dropAllowed;
3935     },
3936
3937     /**
3938      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3939      * This method will be called on every mouse movement while the drag source is over the drop target.
3940      * This default implementation simply returns the dropAllowed config value.
3941      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3942      * @param {Event} e The event
3943      * @param {Object} data An object containing arbitrary data supplied by the drag source
3944      * @return {String} status The CSS class that communicates the drop status back to the source so that the
3945      * underlying {@link Roo.dd.StatusProxy} can be updated
3946      */
3947     notifyOver : function(dd, e, data){
3948         return this.dropAllowed;
3949     },
3950
3951     /**
3952      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3953      * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3954      * overClass (if any) from the drop element.
3955      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3956      * @param {Event} e The event
3957      * @param {Object} data An object containing arbitrary data supplied by the drag source
3958      */
3959     notifyOut : function(dd, e, data){
3960         if(this.overClass){
3961             this.el.removeClass(this.overClass);
3962         }
3963     },
3964
3965     /**
3966      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3967      * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3968      * implementation that does something to process the drop event and returns true so that the drag source's
3969      * repair action does not run.
3970      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3971      * @param {Event} e The event
3972      * @param {Object} data An object containing arbitrary data supplied by the drag source
3973      * @return {Boolean} True if the drop was valid, else false
3974      */
3975     notifyDrop : function(dd, e, data){
3976         return false;
3977     }
3978 });/*
3979  * Based on:
3980  * Ext JS Library 1.1.1
3981  * Copyright(c) 2006-2007, Ext JS, LLC.
3982  *
3983  * Originally Released Under LGPL - original licence link has changed is not relivant.
3984  *
3985  * Fork - LGPL
3986  * <script type="text/javascript">
3987  */
3988
3989
3990 /**
3991  * @class Roo.dd.DragZone
3992  * @extends Roo.dd.DragSource
3993  * This class provides a container DD instance that proxies for multiple child node sources.<br />
3994  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
3995  * @constructor
3996  * @param {String/HTMLElement/Element} el The container element
3997  * @param {Object} config
3998  */
3999 Roo.dd.DragZone = function(el, config){
4000     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4001     if(this.containerScroll){
4002         Roo.dd.ScrollManager.register(this.el);
4003     }
4004 };
4005
4006 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4007     /**
4008      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4009      * for auto scrolling during drag operations.
4010      */
4011     /**
4012      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4013      * method after a failed drop (defaults to "c3daf9" - light blue)
4014      */
4015
4016     /**
4017      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4018      * for a valid target to drag based on the mouse down. Override this method
4019      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4020      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4021      * @param {EventObject} e The mouse down event
4022      * @return {Object} The dragData
4023      */
4024     getDragData : function(e){
4025         return Roo.dd.Registry.getHandleFromEvent(e);
4026     },
4027     
4028     /**
4029      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4030      * this.dragData.ddel
4031      * @param {Number} x The x position of the click on the dragged object
4032      * @param {Number} y The y position of the click on the dragged object
4033      * @return {Boolean} true to continue the drag, false to cancel
4034      */
4035     onInitDrag : function(x, y){
4036         this.proxy.update(this.dragData.ddel.cloneNode(true));
4037         this.onStartDrag(x, y);
4038         return true;
4039     },
4040     
4041     /**
4042      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4043      */
4044     afterRepair : function(){
4045         if(Roo.enableFx){
4046             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4047         }
4048         this.dragging = false;
4049     },
4050
4051     /**
4052      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4053      * the XY of this.dragData.ddel
4054      * @param {EventObject} e The mouse up event
4055      * @return {Array} The xy location (e.g. [100, 200])
4056      */
4057     getRepairXY : function(e){
4058         return Roo.Element.fly(this.dragData.ddel).getXY();  
4059     }
4060 });/*
4061  * Based on:
4062  * Ext JS Library 1.1.1
4063  * Copyright(c) 2006-2007, Ext JS, LLC.
4064  *
4065  * Originally Released Under LGPL - original licence link has changed is not relivant.
4066  *
4067  * Fork - LGPL
4068  * <script type="text/javascript">
4069  */
4070 /**
4071  * @class Roo.dd.DropZone
4072  * @extends Roo.dd.DropTarget
4073  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4074  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4075  * @constructor
4076  * @param {String/HTMLElement/Element} el The container element
4077  * @param {Object} config
4078  */
4079 Roo.dd.DropZone = function(el, config){
4080     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4081 };
4082
4083 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4084     /**
4085      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4086      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4087      * provide your own custom lookup.
4088      * @param {Event} e The event
4089      * @return {Object} data The custom data
4090      */
4091     getTargetFromEvent : function(e){
4092         return Roo.dd.Registry.getTargetFromEvent(e);
4093     },
4094
4095     /**
4096      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4097      * that it has registered.  This method has no default implementation and should be overridden to provide
4098      * node-specific processing if necessary.
4099      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4100      * {@link #getTargetFromEvent} for this node)
4101      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4102      * @param {Event} e The event
4103      * @param {Object} data An object containing arbitrary data supplied by the drag source
4104      */
4105     onNodeEnter : function(n, dd, e, data){
4106         
4107     },
4108
4109     /**
4110      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4111      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4112      * overridden to provide the proper feedback.
4113      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4114      * {@link #getTargetFromEvent} for this node)
4115      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4116      * @param {Event} e The event
4117      * @param {Object} data An object containing arbitrary data supplied by the drag source
4118      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4119      * underlying {@link Roo.dd.StatusProxy} can be updated
4120      */
4121     onNodeOver : function(n, dd, e, data){
4122         return this.dropAllowed;
4123     },
4124
4125     /**
4126      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4127      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4128      * node-specific processing if necessary.
4129      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4130      * {@link #getTargetFromEvent} for this node)
4131      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4132      * @param {Event} e The event
4133      * @param {Object} data An object containing arbitrary data supplied by the drag source
4134      */
4135     onNodeOut : function(n, dd, e, data){
4136         
4137     },
4138
4139     /**
4140      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4141      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4142      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4143      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4144      * {@link #getTargetFromEvent} for this node)
4145      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4146      * @param {Event} e The event
4147      * @param {Object} data An object containing arbitrary data supplied by the drag source
4148      * @return {Boolean} True if the drop was valid, else false
4149      */
4150     onNodeDrop : function(n, dd, e, data){
4151         return false;
4152     },
4153
4154     /**
4155      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4156      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4157      * it should be overridden to provide the proper feedback if necessary.
4158      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4159      * @param {Event} e The event
4160      * @param {Object} data An object containing arbitrary data supplied by the drag source
4161      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4162      * underlying {@link Roo.dd.StatusProxy} can be updated
4163      */
4164     onContainerOver : function(dd, e, data){
4165         return this.dropNotAllowed;
4166     },
4167
4168     /**
4169      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4170      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4171      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4172      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4173      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4174      * @param {Event} e The event
4175      * @param {Object} data An object containing arbitrary data supplied by the drag source
4176      * @return {Boolean} True if the drop was valid, else false
4177      */
4178     onContainerDrop : function(dd, e, data){
4179         return false;
4180     },
4181
4182     /**
4183      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4184      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4185      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4186      * you should override this method and provide a custom implementation.
4187      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4188      * @param {Event} e The event
4189      * @param {Object} data An object containing arbitrary data supplied by the drag source
4190      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4191      * underlying {@link Roo.dd.StatusProxy} can be updated
4192      */
4193     notifyEnter : function(dd, e, data){
4194         return this.dropNotAllowed;
4195     },
4196
4197     /**
4198      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4199      * This method will be called on every mouse movement while the drag source is over the drop zone.
4200      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4201      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4202      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4203      * registered node, it will call {@link #onContainerOver}.
4204      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4205      * @param {Event} e The event
4206      * @param {Object} data An object containing arbitrary data supplied by the drag source
4207      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4208      * underlying {@link Roo.dd.StatusProxy} can be updated
4209      */
4210     notifyOver : function(dd, e, data){
4211         var n = this.getTargetFromEvent(e);
4212         if(!n){ // not over valid drop target
4213             if(this.lastOverNode){
4214                 this.onNodeOut(this.lastOverNode, dd, e, data);
4215                 this.lastOverNode = null;
4216             }
4217             return this.onContainerOver(dd, e, data);
4218         }
4219         if(this.lastOverNode != n){
4220             if(this.lastOverNode){
4221                 this.onNodeOut(this.lastOverNode, dd, e, data);
4222             }
4223             this.onNodeEnter(n, dd, e, data);
4224             this.lastOverNode = n;
4225         }
4226         return this.onNodeOver(n, dd, e, data);
4227     },
4228
4229     /**
4230      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4231      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4232      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4233      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4234      * @param {Event} e The event
4235      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4236      */
4237     notifyOut : function(dd, e, data){
4238         if(this.lastOverNode){
4239             this.onNodeOut(this.lastOverNode, dd, e, data);
4240             this.lastOverNode = null;
4241         }
4242     },
4243
4244     /**
4245      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4246      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4247      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4248      * otherwise it will call {@link #onContainerDrop}.
4249      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4250      * @param {Event} e The event
4251      * @param {Object} data An object containing arbitrary data supplied by the drag source
4252      * @return {Boolean} True if the drop was valid, else false
4253      */
4254     notifyDrop : function(dd, e, data){
4255         if(this.lastOverNode){
4256             this.onNodeOut(this.lastOverNode, dd, e, data);
4257             this.lastOverNode = null;
4258         }
4259         var n = this.getTargetFromEvent(e);
4260         return n ?
4261             this.onNodeDrop(n, dd, e, data) :
4262             this.onContainerDrop(dd, e, data);
4263     },
4264
4265     // private
4266     triggerCacheRefresh : function(){
4267         Roo.dd.DDM.refreshCache(this.groups);
4268     }  
4269 });/*
4270  * Based on:
4271  * Ext JS Library 1.1.1
4272  * Copyright(c) 2006-2007, Ext JS, LLC.
4273  *
4274  * Originally Released Under LGPL - original licence link has changed is not relivant.
4275  *
4276  * Fork - LGPL
4277  * <script type="text/javascript">
4278  */
4279
4280
4281 /**
4282  * @class Roo.data.SortTypes
4283  * @singleton
4284  * Defines the default sorting (casting?) comparison functions used when sorting data.
4285  */
4286 Roo.data.SortTypes = {
4287     /**
4288      * Default sort that does nothing
4289      * @param {Mixed} s The value being converted
4290      * @return {Mixed} The comparison value
4291      */
4292     none : function(s){
4293         return s;
4294     },
4295     
4296     /**
4297      * The regular expression used to strip tags
4298      * @type {RegExp}
4299      * @property
4300      */
4301     stripTagsRE : /<\/?[^>]+>/gi,
4302     
4303     /**
4304      * Strips all HTML tags to sort on text only
4305      * @param {Mixed} s The value being converted
4306      * @return {String} The comparison value
4307      */
4308     asText : function(s){
4309         return String(s).replace(this.stripTagsRE, "");
4310     },
4311     
4312     /**
4313      * Strips all HTML tags to sort on text only - Case insensitive
4314      * @param {Mixed} s The value being converted
4315      * @return {String} The comparison value
4316      */
4317     asUCText : function(s){
4318         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4319     },
4320     
4321     /**
4322      * Case insensitive string
4323      * @param {Mixed} s The value being converted
4324      * @return {String} The comparison value
4325      */
4326     asUCString : function(s) {
4327         return String(s).toUpperCase();
4328     },
4329     
4330     /**
4331      * Date sorting
4332      * @param {Mixed} s The value being converted
4333      * @return {Number} The comparison value
4334      */
4335     asDate : function(s) {
4336         if(!s){
4337             return 0;
4338         }
4339         if(s instanceof Date){
4340             return s.getTime();
4341         }
4342         return Date.parse(String(s));
4343     },
4344     
4345     /**
4346      * Float sorting
4347      * @param {Mixed} s The value being converted
4348      * @return {Float} The comparison value
4349      */
4350     asFloat : function(s) {
4351         var val = parseFloat(String(s).replace(/,/g, ""));
4352         if(isNaN(val)) val = 0;
4353         return val;
4354     },
4355     
4356     /**
4357      * Integer sorting
4358      * @param {Mixed} s The value being converted
4359      * @return {Number} The comparison value
4360      */
4361     asInt : function(s) {
4362         var val = parseInt(String(s).replace(/,/g, ""));
4363         if(isNaN(val)) val = 0;
4364         return val;
4365     }
4366 };/*
4367  * Based on:
4368  * Ext JS Library 1.1.1
4369  * Copyright(c) 2006-2007, Ext JS, LLC.
4370  *
4371  * Originally Released Under LGPL - original licence link has changed is not relivant.
4372  *
4373  * Fork - LGPL
4374  * <script type="text/javascript">
4375  */
4376
4377 /**
4378 * @class Roo.data.Record
4379  * Instances of this class encapsulate both record <em>definition</em> information, and record
4380  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4381  * to access Records cached in an {@link Roo.data.Store} object.<br>
4382  * <p>
4383  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4384  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4385  * objects.<br>
4386  * <p>
4387  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4388  * @constructor
4389  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4390  * {@link #create}. The parameters are the same.
4391  * @param {Array} data An associative Array of data values keyed by the field name.
4392  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4393  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4394  * not specified an integer id is generated.
4395  */
4396 Roo.data.Record = function(data, id){
4397     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4398     this.data = data;
4399 };
4400
4401 /**
4402  * Generate a constructor for a specific record layout.
4403  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4404  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4405  * Each field definition object may contain the following properties: <ul>
4406  * <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,
4407  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4408  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4409  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4410  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4411  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4412  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4413  * this may be omitted.</p></li>
4414  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4415  * <ul><li>auto (Default, implies no conversion)</li>
4416  * <li>string</li>
4417  * <li>int</li>
4418  * <li>float</li>
4419  * <li>boolean</li>
4420  * <li>date</li></ul></p></li>
4421  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4422  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4423  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4424  * by the Reader into an object that will be stored in the Record. It is passed the
4425  * following parameters:<ul>
4426  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4427  * </ul></p></li>
4428  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4429  * </ul>
4430  * <br>usage:<br><pre><code>
4431 var TopicRecord = Roo.data.Record.create(
4432     {name: 'title', mapping: 'topic_title'},
4433     {name: 'author', mapping: 'username'},
4434     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4435     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4436     {name: 'lastPoster', mapping: 'user2'},
4437     {name: 'excerpt', mapping: 'post_text'}
4438 );
4439
4440 var myNewRecord = new TopicRecord({
4441     title: 'Do my job please',
4442     author: 'noobie',
4443     totalPosts: 1,
4444     lastPost: new Date(),
4445     lastPoster: 'Animal',
4446     excerpt: 'No way dude!'
4447 });
4448 myStore.add(myNewRecord);
4449 </code></pre>
4450  * @method create
4451  * @static
4452  */
4453 Roo.data.Record.create = function(o){
4454     var f = function(){
4455         f.superclass.constructor.apply(this, arguments);
4456     };
4457     Roo.extend(f, Roo.data.Record);
4458     var p = f.prototype;
4459     p.fields = new Roo.util.MixedCollection(false, function(field){
4460         return field.name;
4461     });
4462     for(var i = 0, len = o.length; i < len; i++){
4463         p.fields.add(new Roo.data.Field(o[i]));
4464     }
4465     f.getField = function(name){
4466         return p.fields.get(name);  
4467     };
4468     return f;
4469 };
4470
4471 Roo.data.Record.AUTO_ID = 1000;
4472 Roo.data.Record.EDIT = 'edit';
4473 Roo.data.Record.REJECT = 'reject';
4474 Roo.data.Record.COMMIT = 'commit';
4475
4476 Roo.data.Record.prototype = {
4477     /**
4478      * Readonly flag - true if this record has been modified.
4479      * @type Boolean
4480      */
4481     dirty : false,
4482     editing : false,
4483     error: null,
4484     modified: null,
4485
4486     // private
4487     join : function(store){
4488         this.store = store;
4489     },
4490
4491     /**
4492      * Set the named field to the specified value.
4493      * @param {String} name The name of the field to set.
4494      * @param {Object} value The value to set the field to.
4495      */
4496     set : function(name, value){
4497         if(this.data[name] == value){
4498             return;
4499         }
4500         this.dirty = true;
4501         if(!this.modified){
4502             this.modified = {};
4503         }
4504         if(typeof this.modified[name] == 'undefined'){
4505             this.modified[name] = this.data[name];
4506         }
4507         this.data[name] = value;
4508         if(!this.editing){
4509             this.store.afterEdit(this);
4510         }       
4511     },
4512
4513     /**
4514      * Get the value of the named field.
4515      * @param {String} name The name of the field to get the value of.
4516      * @return {Object} The value of the field.
4517      */
4518     get : function(name){
4519         return this.data[name]; 
4520     },
4521
4522     // private
4523     beginEdit : function(){
4524         this.editing = true;
4525         this.modified = {}; 
4526     },
4527
4528     // private
4529     cancelEdit : function(){
4530         this.editing = false;
4531         delete this.modified;
4532     },
4533
4534     // private
4535     endEdit : function(){
4536         this.editing = false;
4537         if(this.dirty && this.store){
4538             this.store.afterEdit(this);
4539         }
4540     },
4541
4542     /**
4543      * Usually called by the {@link Roo.data.Store} which owns the Record.
4544      * Rejects all changes made to the Record since either creation, or the last commit operation.
4545      * Modified fields are reverted to their original values.
4546      * <p>
4547      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4548      * of reject operations.
4549      */
4550     reject : function(){
4551         var m = this.modified;
4552         for(var n in m){
4553             if(typeof m[n] != "function"){
4554                 this.data[n] = m[n];
4555             }
4556         }
4557         this.dirty = false;
4558         delete this.modified;
4559         this.editing = false;
4560         if(this.store){
4561             this.store.afterReject(this);
4562         }
4563     },
4564
4565     /**
4566      * Usually called by the {@link Roo.data.Store} which owns the Record.
4567      * Commits all changes made to the Record since either creation, or the last commit operation.
4568      * <p>
4569      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4570      * of commit operations.
4571      */
4572     commit : function(){
4573         this.dirty = false;
4574         delete this.modified;
4575         this.editing = false;
4576         if(this.store){
4577             this.store.afterCommit(this);
4578         }
4579     },
4580
4581     // private
4582     hasError : function(){
4583         return this.error != null;
4584     },
4585
4586     // private
4587     clearError : function(){
4588         this.error = null;
4589     },
4590
4591     /**
4592      * Creates a copy of this record.
4593      * @param {String} id (optional) A new record id if you don't want to use this record's id
4594      * @return {Record}
4595      */
4596     copy : function(newId) {
4597         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4598     }
4599 };/*
4600  * Based on:
4601  * Ext JS Library 1.1.1
4602  * Copyright(c) 2006-2007, Ext JS, LLC.
4603  *
4604  * Originally Released Under LGPL - original licence link has changed is not relivant.
4605  *
4606  * Fork - LGPL
4607  * <script type="text/javascript">
4608  */
4609
4610
4611
4612 /**
4613  * @class Roo.data.Store
4614  * @extends Roo.util.Observable
4615  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4616  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4617  * <p>
4618  * 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
4619  * has no knowledge of the format of the data returned by the Proxy.<br>
4620  * <p>
4621  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4622  * instances from the data object. These records are cached and made available through accessor functions.
4623  * @constructor
4624  * Creates a new Store.
4625  * @param {Object} config A config object containing the objects needed for the Store to access data,
4626  * and read the data into Records.
4627  */
4628 Roo.data.Store = function(config){
4629     this.data = new Roo.util.MixedCollection(false);
4630     this.data.getKey = function(o){
4631         return o.id;
4632     };
4633     this.baseParams = {};
4634     // private
4635     this.paramNames = {
4636         "start" : "start",
4637         "limit" : "limit",
4638         "sort" : "sort",
4639         "dir" : "dir"
4640     };
4641
4642     if(config && config.data){
4643         this.inlineData = config.data;
4644         delete config.data;
4645     }
4646
4647     Roo.apply(this, config);
4648     
4649     if(this.reader){ // reader passed
4650         this.reader = Roo.factory(this.reader, Roo.data);
4651         this.reader.xmodule = this.xmodule || false;
4652         if(!this.recordType){
4653             this.recordType = this.reader.recordType;
4654         }
4655         if(this.reader.onMetaChange){
4656             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4657         }
4658     }
4659
4660     if(this.recordType){
4661         this.fields = this.recordType.prototype.fields;
4662     }
4663     this.modified = [];
4664
4665     this.addEvents({
4666         /**
4667          * @event datachanged
4668          * Fires when the data cache has changed, and a widget which is using this Store
4669          * as a Record cache should refresh its view.
4670          * @param {Store} this
4671          */
4672         datachanged : true,
4673         /**
4674          * @event metachange
4675          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4676          * @param {Store} this
4677          * @param {Object} meta The JSON metadata
4678          */
4679         metachange : true,
4680         /**
4681          * @event add
4682          * Fires when Records have been added to the Store
4683          * @param {Store} this
4684          * @param {Roo.data.Record[]} records The array of Records added
4685          * @param {Number} index The index at which the record(s) were added
4686          */
4687         add : true,
4688         /**
4689          * @event remove
4690          * Fires when a Record has been removed from the Store
4691          * @param {Store} this
4692          * @param {Roo.data.Record} record The Record that was removed
4693          * @param {Number} index The index at which the record was removed
4694          */
4695         remove : true,
4696         /**
4697          * @event update
4698          * Fires when a Record has been updated
4699          * @param {Store} this
4700          * @param {Roo.data.Record} record The Record that was updated
4701          * @param {String} operation The update operation being performed.  Value may be one of:
4702          * <pre><code>
4703  Roo.data.Record.EDIT
4704  Roo.data.Record.REJECT
4705  Roo.data.Record.COMMIT
4706          * </code></pre>
4707          */
4708         update : true,
4709         /**
4710          * @event clear
4711          * Fires when the data cache has been cleared.
4712          * @param {Store} this
4713          */
4714         clear : true,
4715         /**
4716          * @event beforeload
4717          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4718          * the load action will be canceled.
4719          * @param {Store} this
4720          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4721          */
4722         beforeload : true,
4723         /**
4724          * @event load
4725          * Fires after a new set of Records has been loaded.
4726          * @param {Store} this
4727          * @param {Roo.data.Record[]} records The Records that were loaded
4728          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4729          */
4730         load : true,
4731         /**
4732          * @event loadexception
4733          * Fires if an exception occurs in the Proxy during loading.
4734          * Called with the signature of the Proxy's "loadexception" event.
4735          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4736          * 
4737          * @param {Proxy} 
4738          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4739          * @param {Object} load options 
4740          * @param {Object} jsonData from your request (normally this contains the Exception)
4741          */
4742         loadexception : true
4743     });
4744     
4745     if(this.proxy){
4746         this.proxy = Roo.factory(this.proxy, Roo.data);
4747         this.proxy.xmodule = this.xmodule || false;
4748         this.relayEvents(this.proxy,  ["loadexception"]);
4749     }
4750     this.sortToggle = {};
4751
4752     Roo.data.Store.superclass.constructor.call(this);
4753
4754     if(this.inlineData){
4755         this.loadData(this.inlineData);
4756         delete this.inlineData;
4757     }
4758 };
4759 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4760      /**
4761     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4762     * without a remote query - used by combo/forms at present.
4763     */
4764     
4765     /**
4766     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4767     */
4768     /**
4769     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4770     */
4771     /**
4772     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4773     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4774     */
4775     /**
4776     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4777     * on any HTTP request
4778     */
4779     /**
4780     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4781     */
4782     /**
4783     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4784     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4785     */
4786     remoteSort : false,
4787
4788     /**
4789     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4790      * loaded or when a record is removed. (defaults to false).
4791     */
4792     pruneModifiedRecords : false,
4793
4794     // private
4795     lastOptions : null,
4796
4797     /**
4798      * Add Records to the Store and fires the add event.
4799      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4800      */
4801     add : function(records){
4802         records = [].concat(records);
4803         for(var i = 0, len = records.length; i < len; i++){
4804             records[i].join(this);
4805         }
4806         var index = this.data.length;
4807         this.data.addAll(records);
4808         this.fireEvent("add", this, records, index);
4809     },
4810
4811     /**
4812      * Remove a Record from the Store and fires the remove event.
4813      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4814      */
4815     remove : function(record){
4816         var index = this.data.indexOf(record);
4817         this.data.removeAt(index);
4818         if(this.pruneModifiedRecords){
4819             this.modified.remove(record);
4820         }
4821         this.fireEvent("remove", this, record, index);
4822     },
4823
4824     /**
4825      * Remove all Records from the Store and fires the clear event.
4826      */
4827     removeAll : function(){
4828         this.data.clear();
4829         if(this.pruneModifiedRecords){
4830             this.modified = [];
4831         }
4832         this.fireEvent("clear", this);
4833     },
4834
4835     /**
4836      * Inserts Records to the Store at the given index and fires the add event.
4837      * @param {Number} index The start index at which to insert the passed Records.
4838      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4839      */
4840     insert : function(index, records){
4841         records = [].concat(records);
4842         for(var i = 0, len = records.length; i < len; i++){
4843             this.data.insert(index, records[i]);
4844             records[i].join(this);
4845         }
4846         this.fireEvent("add", this, records, index);
4847     },
4848
4849     /**
4850      * Get the index within the cache of the passed Record.
4851      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4852      * @return {Number} The index of the passed Record. Returns -1 if not found.
4853      */
4854     indexOf : function(record){
4855         return this.data.indexOf(record);
4856     },
4857
4858     /**
4859      * Get the index within the cache of the Record with the passed id.
4860      * @param {String} id The id of the Record to find.
4861      * @return {Number} The index of the Record. Returns -1 if not found.
4862      */
4863     indexOfId : function(id){
4864         return this.data.indexOfKey(id);
4865     },
4866
4867     /**
4868      * Get the Record with the specified id.
4869      * @param {String} id The id of the Record to find.
4870      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4871      */
4872     getById : function(id){
4873         return this.data.key(id);
4874     },
4875
4876     /**
4877      * Get the Record at the specified index.
4878      * @param {Number} index The index of the Record to find.
4879      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4880      */
4881     getAt : function(index){
4882         return this.data.itemAt(index);
4883     },
4884
4885     /**
4886      * Returns a range of Records between specified indices.
4887      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4888      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4889      * @return {Roo.data.Record[]} An array of Records
4890      */
4891     getRange : function(start, end){
4892         return this.data.getRange(start, end);
4893     },
4894
4895     // private
4896     storeOptions : function(o){
4897         o = Roo.apply({}, o);
4898         delete o.callback;
4899         delete o.scope;
4900         this.lastOptions = o;
4901     },
4902
4903     /**
4904      * Loads the Record cache from the configured Proxy using the configured Reader.
4905      * <p>
4906      * If using remote paging, then the first load call must specify the <em>start</em>
4907      * and <em>limit</em> properties in the options.params property to establish the initial
4908      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4909      * <p>
4910      * <strong>It is important to note that for remote data sources, loading is asynchronous,
4911      * and this call will return before the new data has been loaded. Perform any post-processing
4912      * in a callback function, or in a "load" event handler.</strong>
4913      * <p>
4914      * @param {Object} options An object containing properties which control loading options:<ul>
4915      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
4916      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
4917      * passed the following arguments:<ul>
4918      * <li>r : Roo.data.Record[]</li>
4919      * <li>options: Options object from the load call</li>
4920      * <li>success: Boolean success indicator</li></ul></li>
4921      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
4922      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
4923      * </ul>
4924      */
4925     load : function(options){
4926         options = options || {};
4927         if(this.fireEvent("beforeload", this, options) !== false){
4928             this.storeOptions(options);
4929             var p = Roo.apply(options.params || {}, this.baseParams);
4930             // if meta was not loaded from remote source.. try requesting it.
4931             if (!this.reader.metaFromRemote) {
4932                 p._requestMeta = 1;
4933             }
4934             if(this.sortInfo && this.remoteSort){
4935                 var pn = this.paramNames;
4936                 p[pn["sort"]] = this.sortInfo.field;
4937                 p[pn["dir"]] = this.sortInfo.direction;
4938             }
4939             this.proxy.load(p, this.reader, this.loadRecords, this, options);
4940         }
4941     },
4942
4943     /**
4944      * Reloads the Record cache from the configured Proxy using the configured Reader and
4945      * the options from the last load operation performed.
4946      * @param {Object} options (optional) An object containing properties which may override the options
4947      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
4948      * the most recently used options are reused).
4949      */
4950     reload : function(options){
4951         this.load(Roo.applyIf(options||{}, this.lastOptions));
4952     },
4953
4954     // private
4955     // Called as a callback by the Reader during a load operation.
4956     loadRecords : function(o, options, success){
4957         if(!o || success === false){
4958             if(success !== false){
4959                 this.fireEvent("load", this, [], options);
4960             }
4961             if(options.callback){
4962                 options.callback.call(options.scope || this, [], options, false);
4963             }
4964             return;
4965         }
4966         // if data returned failure - throw an exception.
4967         if (o.success === false) {
4968             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
4969             return;
4970         }
4971         var r = o.records, t = o.totalRecords || r.length;
4972         if(!options || options.add !== true){
4973             if(this.pruneModifiedRecords){
4974                 this.modified = [];
4975             }
4976             for(var i = 0, len = r.length; i < len; i++){
4977                 r[i].join(this);
4978             }
4979             if(this.snapshot){
4980                 this.data = this.snapshot;
4981                 delete this.snapshot;
4982             }
4983             this.data.clear();
4984             this.data.addAll(r);
4985             this.totalLength = t;
4986             this.applySort();
4987             this.fireEvent("datachanged", this);
4988         }else{
4989             this.totalLength = Math.max(t, this.data.length+r.length);
4990             this.add(r);
4991         }
4992         this.fireEvent("load", this, r, options);
4993         if(options.callback){
4994             options.callback.call(options.scope || this, r, options, true);
4995         }
4996     },
4997
4998     /**
4999      * Loads data from a passed data block. A Reader which understands the format of the data
5000      * must have been configured in the constructor.
5001      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5002      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5003      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5004      */
5005     loadData : function(o, append){
5006         var r = this.reader.readRecords(o);
5007         this.loadRecords(r, {add: append}, true);
5008     },
5009
5010     /**
5011      * Gets the number of cached records.
5012      * <p>
5013      * <em>If using paging, this may not be the total size of the dataset. If the data object
5014      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5015      * the data set size</em>
5016      */
5017     getCount : function(){
5018         return this.data.length || 0;
5019     },
5020
5021     /**
5022      * Gets the total number of records in the dataset as returned by the server.
5023      * <p>
5024      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5025      * the dataset size</em>
5026      */
5027     getTotalCount : function(){
5028         return this.totalLength || 0;
5029     },
5030
5031     /**
5032      * Returns the sort state of the Store as an object with two properties:
5033      * <pre><code>
5034  field {String} The name of the field by which the Records are sorted
5035  direction {String} The sort order, "ASC" or "DESC"
5036      * </code></pre>
5037      */
5038     getSortState : function(){
5039         return this.sortInfo;
5040     },
5041
5042     // private
5043     applySort : function(){
5044         if(this.sortInfo && !this.remoteSort){
5045             var s = this.sortInfo, f = s.field;
5046             var st = this.fields.get(f).sortType;
5047             var fn = function(r1, r2){
5048                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5049                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5050             };
5051             this.data.sort(s.direction, fn);
5052             if(this.snapshot && this.snapshot != this.data){
5053                 this.snapshot.sort(s.direction, fn);
5054             }
5055         }
5056     },
5057
5058     /**
5059      * Sets the default sort column and order to be used by the next load operation.
5060      * @param {String} fieldName The name of the field to sort by.
5061      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5062      */
5063     setDefaultSort : function(field, dir){
5064         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5065     },
5066
5067     /**
5068      * Sort the Records.
5069      * If remote sorting is used, the sort is performed on the server, and the cache is
5070      * reloaded. If local sorting is used, the cache is sorted internally.
5071      * @param {String} fieldName The name of the field to sort by.
5072      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5073      */
5074     sort : function(fieldName, dir){
5075         var f = this.fields.get(fieldName);
5076         if(!dir){
5077             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
5078                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5079             }else{
5080                 dir = f.sortDir;
5081             }
5082         }
5083         this.sortToggle[f.name] = dir;
5084         this.sortInfo = {field: f.name, direction: dir};
5085         if(!this.remoteSort){
5086             this.applySort();
5087             this.fireEvent("datachanged", this);
5088         }else{
5089             this.load(this.lastOptions);
5090         }
5091     },
5092
5093     /**
5094      * Calls the specified function for each of the Records in the cache.
5095      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5096      * Returning <em>false</em> aborts and exits the iteration.
5097      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5098      */
5099     each : function(fn, scope){
5100         this.data.each(fn, scope);
5101     },
5102
5103     /**
5104      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5105      * (e.g., during paging).
5106      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5107      */
5108     getModifiedRecords : function(){
5109         return this.modified;
5110     },
5111
5112     // private
5113     createFilterFn : function(property, value, anyMatch){
5114         if(!value.exec){ // not a regex
5115             value = String(value);
5116             if(value.length == 0){
5117                 return false;
5118             }
5119             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5120         }
5121         return function(r){
5122             return value.test(r.data[property]);
5123         };
5124     },
5125
5126     /**
5127      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5128      * @param {String} property A field on your records
5129      * @param {Number} start The record index to start at (defaults to 0)
5130      * @param {Number} end The last record index to include (defaults to length - 1)
5131      * @return {Number} The sum
5132      */
5133     sum : function(property, start, end){
5134         var rs = this.data.items, v = 0;
5135         start = start || 0;
5136         end = (end || end === 0) ? end : rs.length-1;
5137
5138         for(var i = start; i <= end; i++){
5139             v += (rs[i].data[property] || 0);
5140         }
5141         return v;
5142     },
5143
5144     /**
5145      * Filter the records by a specified property.
5146      * @param {String} field A field on your records
5147      * @param {String/RegExp} value Either a string that the field
5148      * should start with or a RegExp to test against the field
5149      * @param {Boolean} anyMatch True to match any part not just the beginning
5150      */
5151     filter : function(property, value, anyMatch){
5152         var fn = this.createFilterFn(property, value, anyMatch);
5153         return fn ? this.filterBy(fn) : this.clearFilter();
5154     },
5155
5156     /**
5157      * Filter by a function. The specified function will be called with each
5158      * record in this data source. If the function returns true the record is included,
5159      * otherwise it is filtered.
5160      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5161      * @param {Object} scope (optional) The scope of the function (defaults to this)
5162      */
5163     filterBy : function(fn, scope){
5164         this.snapshot = this.snapshot || this.data;
5165         this.data = this.queryBy(fn, scope||this);
5166         this.fireEvent("datachanged", this);
5167     },
5168
5169     /**
5170      * Query the records by a specified property.
5171      * @param {String} field A field on your records
5172      * @param {String/RegExp} value Either a string that the field
5173      * should start with or a RegExp to test against the field
5174      * @param {Boolean} anyMatch True to match any part not just the beginning
5175      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5176      */
5177     query : function(property, value, anyMatch){
5178         var fn = this.createFilterFn(property, value, anyMatch);
5179         return fn ? this.queryBy(fn) : this.data.clone();
5180     },
5181
5182     /**
5183      * Query by a function. The specified function will be called with each
5184      * record in this data source. If the function returns true the record is included
5185      * in the results.
5186      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5187      * @param {Object} scope (optional) The scope of the function (defaults to this)
5188       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5189      **/
5190     queryBy : function(fn, scope){
5191         var data = this.snapshot || this.data;
5192         return data.filterBy(fn, scope||this);
5193     },
5194
5195     /**
5196      * Collects unique values for a particular dataIndex from this store.
5197      * @param {String} dataIndex The property to collect
5198      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5199      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5200      * @return {Array} An array of the unique values
5201      **/
5202     collect : function(dataIndex, allowNull, bypassFilter){
5203         var d = (bypassFilter === true && this.snapshot) ?
5204                 this.snapshot.items : this.data.items;
5205         var v, sv, r = [], l = {};
5206         for(var i = 0, len = d.length; i < len; i++){
5207             v = d[i].data[dataIndex];
5208             sv = String(v);
5209             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5210                 l[sv] = true;
5211                 r[r.length] = v;
5212             }
5213         }
5214         return r;
5215     },
5216
5217     /**
5218      * Revert to a view of the Record cache with no filtering applied.
5219      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5220      */
5221     clearFilter : function(suppressEvent){
5222         if(this.snapshot && this.snapshot != this.data){
5223             this.data = this.snapshot;
5224             delete this.snapshot;
5225             if(suppressEvent !== true){
5226                 this.fireEvent("datachanged", this);
5227             }
5228         }
5229     },
5230
5231     // private
5232     afterEdit : function(record){
5233         if(this.modified.indexOf(record) == -1){
5234             this.modified.push(record);
5235         }
5236         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5237     },
5238
5239     // private
5240     afterReject : function(record){
5241         this.modified.remove(record);
5242         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5243     },
5244
5245     // private
5246     afterCommit : function(record){
5247         this.modified.remove(record);
5248         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5249     },
5250
5251     /**
5252      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5253      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5254      */
5255     commitChanges : function(){
5256         var m = this.modified.slice(0);
5257         this.modified = [];
5258         for(var i = 0, len = m.length; i < len; i++){
5259             m[i].commit();
5260         }
5261     },
5262
5263     /**
5264      * Cancel outstanding changes on all changed records.
5265      */
5266     rejectChanges : function(){
5267         var m = this.modified.slice(0);
5268         this.modified = [];
5269         for(var i = 0, len = m.length; i < len; i++){
5270             m[i].reject();
5271         }
5272     },
5273
5274     onMetaChange : function(meta, rtype, o){
5275         this.recordType = rtype;
5276         this.fields = rtype.prototype.fields;
5277         delete this.snapshot;
5278         this.sortInfo = meta.sortInfo || this.sortInfo;
5279         this.modified = [];
5280         this.fireEvent('metachange', this, this.reader.meta);
5281     }
5282 });/*
5283  * Based on:
5284  * Ext JS Library 1.1.1
5285  * Copyright(c) 2006-2007, Ext JS, LLC.
5286  *
5287  * Originally Released Under LGPL - original licence link has changed is not relivant.
5288  *
5289  * Fork - LGPL
5290  * <script type="text/javascript">
5291  */
5292
5293 /**
5294  * @class Roo.data.SimpleStore
5295  * @extends Roo.data.Store
5296  * Small helper class to make creating Stores from Array data easier.
5297  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5298  * @cfg {Array} fields An array of field definition objects, or field name strings.
5299  * @cfg {Array} data The multi-dimensional array of data
5300  * @constructor
5301  * @param {Object} config
5302  */
5303 Roo.data.SimpleStore = function(config){
5304     Roo.data.SimpleStore.superclass.constructor.call(this, {
5305         isLocal : true,
5306         reader: new Roo.data.ArrayReader({
5307                 id: config.id
5308             },
5309             Roo.data.Record.create(config.fields)
5310         ),
5311         proxy : new Roo.data.MemoryProxy(config.data)
5312     });
5313     this.load();
5314 };
5315 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5316  * Based on:
5317  * Ext JS Library 1.1.1
5318  * Copyright(c) 2006-2007, Ext JS, LLC.
5319  *
5320  * Originally Released Under LGPL - original licence link has changed is not relivant.
5321  *
5322  * Fork - LGPL
5323  * <script type="text/javascript">
5324  */
5325
5326 /**
5327 /**
5328  * @extends Roo.data.Store
5329  * @class Roo.data.JsonStore
5330  * Small helper class to make creating Stores for JSON data easier. <br/>
5331 <pre><code>
5332 var store = new Roo.data.JsonStore({
5333     url: 'get-images.php',
5334     root: 'images',
5335     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5336 });
5337 </code></pre>
5338  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5339  * JsonReader and HttpProxy (unless inline data is provided).</b>
5340  * @cfg {Array} fields An array of field definition objects, or field name strings.
5341  * @constructor
5342  * @param {Object} config
5343  */
5344 Roo.data.JsonStore = function(c){
5345     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5346         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5347         reader: new Roo.data.JsonReader(c, c.fields)
5348     }));
5349 };
5350 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5351  * Based on:
5352  * Ext JS Library 1.1.1
5353  * Copyright(c) 2006-2007, Ext JS, LLC.
5354  *
5355  * Originally Released Under LGPL - original licence link has changed is not relivant.
5356  *
5357  * Fork - LGPL
5358  * <script type="text/javascript">
5359  */
5360
5361  
5362 Roo.data.Field = function(config){
5363     if(typeof config == "string"){
5364         config = {name: config};
5365     }
5366     Roo.apply(this, config);
5367     
5368     if(!this.type){
5369         this.type = "auto";
5370     }
5371     
5372     var st = Roo.data.SortTypes;
5373     // named sortTypes are supported, here we look them up
5374     if(typeof this.sortType == "string"){
5375         this.sortType = st[this.sortType];
5376     }
5377     
5378     // set default sortType for strings and dates
5379     if(!this.sortType){
5380         switch(this.type){
5381             case "string":
5382                 this.sortType = st.asUCString;
5383                 break;
5384             case "date":
5385                 this.sortType = st.asDate;
5386                 break;
5387             default:
5388                 this.sortType = st.none;
5389         }
5390     }
5391
5392     // define once
5393     var stripRe = /[\$,%]/g;
5394
5395     // prebuilt conversion function for this field, instead of
5396     // switching every time we're reading a value
5397     if(!this.convert){
5398         var cv, dateFormat = this.dateFormat;
5399         switch(this.type){
5400             case "":
5401             case "auto":
5402             case undefined:
5403                 cv = function(v){ return v; };
5404                 break;
5405             case "string":
5406                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5407                 break;
5408             case "int":
5409                 cv = function(v){
5410                     return v !== undefined && v !== null && v !== '' ?
5411                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5412                     };
5413                 break;
5414             case "float":
5415                 cv = function(v){
5416                     return v !== undefined && v !== null && v !== '' ?
5417                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5418                     };
5419                 break;
5420             case "bool":
5421             case "boolean":
5422                 cv = function(v){ return v === true || v === "true" || v == 1; };
5423                 break;
5424             case "date":
5425                 cv = function(v){
5426                     if(!v){
5427                         return '';
5428                     }
5429                     if(v instanceof Date){
5430                         return v;
5431                     }
5432                     if(dateFormat){
5433                         if(dateFormat == "timestamp"){
5434                             return new Date(v*1000);
5435                         }
5436                         return Date.parseDate(v, dateFormat);
5437                     }
5438                     var parsed = Date.parse(v);
5439                     return parsed ? new Date(parsed) : null;
5440                 };
5441              break;
5442             
5443         }
5444         this.convert = cv;
5445     }
5446 };
5447
5448 Roo.data.Field.prototype = {
5449     dateFormat: null,
5450     defaultValue: "",
5451     mapping: null,
5452     sortType : null,
5453     sortDir : "ASC"
5454 };/*
5455  * Based on:
5456  * Ext JS Library 1.1.1
5457  * Copyright(c) 2006-2007, Ext JS, LLC.
5458  *
5459  * Originally Released Under LGPL - original licence link has changed is not relivant.
5460  *
5461  * Fork - LGPL
5462  * <script type="text/javascript">
5463  */
5464  
5465 // Base class for reading structured data from a data source.  This class is intended to be
5466 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5467
5468 /**
5469  * @class Roo.data.DataReader
5470  * Base class for reading structured data from a data source.  This class is intended to be
5471  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5472  */
5473
5474 Roo.data.DataReader = function(meta, recordType){
5475     
5476     this.meta = meta;
5477     
5478     this.recordType = recordType instanceof Array ? 
5479         Roo.data.Record.create(recordType) : recordType;
5480 };
5481
5482 Roo.data.DataReader.prototype = {
5483      /**
5484      * Create an empty record
5485      * @param {Object} data (optional) - overlay some values
5486      * @return {Roo.data.Record} record created.
5487      */
5488     newRow :  function(d) {
5489         var da =  {};
5490         this.recordType.prototype.fields.each(function(c) {
5491             switch( c.type) {
5492                 case 'int' : da[c.name] = 0; break;
5493                 case 'date' : da[c.name] = new Date(); break;
5494                 case 'float' : da[c.name] = 0.0; break;
5495                 case 'boolean' : da[c.name] = false; break;
5496                 default : da[c.name] = ""; break;
5497             }
5498             
5499         });
5500         return new this.recordType(Roo.apply(da, d));
5501     }
5502     
5503 };/*
5504  * Based on:
5505  * Ext JS Library 1.1.1
5506  * Copyright(c) 2006-2007, Ext JS, LLC.
5507  *
5508  * Originally Released Under LGPL - original licence link has changed is not relivant.
5509  *
5510  * Fork - LGPL
5511  * <script type="text/javascript">
5512  */
5513
5514 /**
5515  * @class Roo.data.DataProxy
5516  * @extends Roo.data.Observable
5517  * This class is an abstract base class for implementations which provide retrieval of
5518  * unformatted data objects.<br>
5519  * <p>
5520  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5521  * (of the appropriate type which knows how to parse the data object) to provide a block of
5522  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5523  * <p>
5524  * Custom implementations must implement the load method as described in
5525  * {@link Roo.data.HttpProxy#load}.
5526  */
5527 Roo.data.DataProxy = function(){
5528     this.addEvents({
5529         /**
5530          * @event beforeload
5531          * Fires before a network request is made to retrieve a data object.
5532          * @param {Object} This DataProxy object.
5533          * @param {Object} params The params parameter to the load function.
5534          */
5535         beforeload : true,
5536         /**
5537          * @event load
5538          * Fires before the load method's callback is called.
5539          * @param {Object} This DataProxy object.
5540          * @param {Object} o The data object.
5541          * @param {Object} arg The callback argument object passed to the load function.
5542          */
5543         load : true,
5544         /**
5545          * @event loadexception
5546          * Fires if an Exception occurs during data retrieval.
5547          * @param {Object} This DataProxy object.
5548          * @param {Object} o The data object.
5549          * @param {Object} arg The callback argument object passed to the load function.
5550          * @param {Object} e The Exception.
5551          */
5552         loadexception : true
5553     });
5554     Roo.data.DataProxy.superclass.constructor.call(this);
5555 };
5556
5557 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5558
5559     /**
5560      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5561      */
5562 /*
5563  * Based on:
5564  * Ext JS Library 1.1.1
5565  * Copyright(c) 2006-2007, Ext JS, LLC.
5566  *
5567  * Originally Released Under LGPL - original licence link has changed is not relivant.
5568  *
5569  * Fork - LGPL
5570  * <script type="text/javascript">
5571  */
5572 /**
5573  * @class Roo.data.MemoryProxy
5574  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5575  * to the Reader when its load method is called.
5576  * @constructor
5577  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5578  */
5579 Roo.data.MemoryProxy = function(data){
5580     if (data.data) {
5581         data = data.data;
5582     }
5583     Roo.data.MemoryProxy.superclass.constructor.call(this);
5584     this.data = data;
5585 };
5586
5587 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5588     /**
5589      * Load data from the requested source (in this case an in-memory
5590      * data object passed to the constructor), read the data object into
5591      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5592      * process that block using the passed callback.
5593      * @param {Object} params This parameter is not used by the MemoryProxy class.
5594      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5595      * object into a block of Roo.data.Records.
5596      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5597      * The function must be passed <ul>
5598      * <li>The Record block object</li>
5599      * <li>The "arg" argument from the load function</li>
5600      * <li>A boolean success indicator</li>
5601      * </ul>
5602      * @param {Object} scope The scope in which to call the callback
5603      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5604      */
5605     load : function(params, reader, callback, scope, arg){
5606         params = params || {};
5607         var result;
5608         try {
5609             result = reader.readRecords(this.data);
5610         }catch(e){
5611             this.fireEvent("loadexception", this, arg, null, e);
5612             callback.call(scope, null, arg, false);
5613             return;
5614         }
5615         callback.call(scope, result, arg, true);
5616     },
5617     
5618     // private
5619     update : function(params, records){
5620         
5621     }
5622 });/*
5623  * Based on:
5624  * Ext JS Library 1.1.1
5625  * Copyright(c) 2006-2007, Ext JS, LLC.
5626  *
5627  * Originally Released Under LGPL - original licence link has changed is not relivant.
5628  *
5629  * Fork - LGPL
5630  * <script type="text/javascript">
5631  */
5632 /**
5633  * @class Roo.data.HttpProxy
5634  * @extends Roo.data.DataProxy
5635  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5636  * configured to reference a certain URL.<br><br>
5637  * <p>
5638  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5639  * from which the running page was served.<br><br>
5640  * <p>
5641  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5642  * <p>
5643  * Be aware that to enable the browser to parse an XML document, the server must set
5644  * the Content-Type header in the HTTP response to "text/xml".
5645  * @constructor
5646  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5647  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5648  * will be used to make the request.
5649  */
5650 Roo.data.HttpProxy = function(conn){
5651     Roo.data.HttpProxy.superclass.constructor.call(this);
5652     // is conn a conn config or a real conn?
5653     this.conn = conn;
5654     this.useAjax = !conn || !conn.events;
5655   
5656 };
5657
5658 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5659     // thse are take from connection...
5660     
5661     /**
5662      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5663      */
5664     /**
5665      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5666      * extra parameters to each request made by this object. (defaults to undefined)
5667      */
5668     /**
5669      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5670      *  to each request made by this object. (defaults to undefined)
5671      */
5672     /**
5673      * @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)
5674      */
5675     /**
5676      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5677      */
5678      /**
5679      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5680      * @type Boolean
5681      */
5682   
5683
5684     /**
5685      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5686      * @type Boolean
5687      */
5688     /**
5689      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5690      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5691      * a finer-grained basis than the DataProxy events.
5692      */
5693     getConnection : function(){
5694         return this.useAjax ? Roo.Ajax : this.conn;
5695     },
5696
5697     /**
5698      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5699      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5700      * process that block using the passed callback.
5701      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5702      * for the request to the remote server.
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         if(this.fireEvent("beforeload", this, params) !== false){
5716             var  o = {
5717                 params : params || {},
5718                 request: {
5719                     callback : callback,
5720                     scope : scope,
5721                     arg : arg
5722                 },
5723                 reader: reader,
5724                 callback : this.loadResponse,
5725                 scope: this
5726             };
5727             if(this.useAjax){
5728                 Roo.applyIf(o, this.conn);
5729                 if(this.activeRequest){
5730                     Roo.Ajax.abort(this.activeRequest);
5731                 }
5732                 this.activeRequest = Roo.Ajax.request(o);
5733             }else{
5734                 this.conn.request(o);
5735             }
5736         }else{
5737             callback.call(scope||this, null, arg, false);
5738         }
5739     },
5740
5741     // private
5742     loadResponse : function(o, success, response){
5743         delete this.activeRequest;
5744         if(!success){
5745             this.fireEvent("loadexception", this, o, response);
5746             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5747             return;
5748         }
5749         var result;
5750         try {
5751             result = o.reader.read(response);
5752         }catch(e){
5753             this.fireEvent("loadexception", this, o, response, e);
5754             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5755             return;
5756         }
5757         
5758         this.fireEvent("load", this, o, o.request.arg);
5759         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5760     },
5761
5762     // private
5763     update : function(dataSet){
5764
5765     },
5766
5767     // private
5768     updateResponse : function(dataSet){
5769
5770     }
5771 });/*
5772  * Based on:
5773  * Ext JS Library 1.1.1
5774  * Copyright(c) 2006-2007, Ext JS, LLC.
5775  *
5776  * Originally Released Under LGPL - original licence link has changed is not relivant.
5777  *
5778  * Fork - LGPL
5779  * <script type="text/javascript">
5780  */
5781
5782 /**
5783  * @class Roo.data.ScriptTagProxy
5784  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5785  * other than the originating domain of the running page.<br><br>
5786  * <p>
5787  * <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
5788  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5789  * <p>
5790  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5791  * source code that is used as the source inside a &lt;script> tag.<br><br>
5792  * <p>
5793  * In order for the browser to process the returned data, the server must wrap the data object
5794  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5795  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5796  * depending on whether the callback name was passed:
5797  * <p>
5798  * <pre><code>
5799 boolean scriptTag = false;
5800 String cb = request.getParameter("callback");
5801 if (cb != null) {
5802     scriptTag = true;
5803     response.setContentType("text/javascript");
5804 } else {
5805     response.setContentType("application/x-json");
5806 }
5807 Writer out = response.getWriter();
5808 if (scriptTag) {
5809     out.write(cb + "(");
5810 }
5811 out.print(dataBlock.toJsonString());
5812 if (scriptTag) {
5813     out.write(");");
5814 }
5815 </pre></code>
5816  *
5817  * @constructor
5818  * @param {Object} config A configuration object.
5819  */
5820 Roo.data.ScriptTagProxy = function(config){
5821     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5822     Roo.apply(this, config);
5823     this.head = document.getElementsByTagName("head")[0];
5824 };
5825
5826 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5827
5828 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5829     /**
5830      * @cfg {String} url The URL from which to request the data object.
5831      */
5832     /**
5833      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5834      */
5835     timeout : 30000,
5836     /**
5837      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5838      * the server the name of the callback function set up by the load call to process the returned data object.
5839      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5840      * javascript output which calls this named function passing the data object as its only parameter.
5841      */
5842     callbackParam : "callback",
5843     /**
5844      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5845      * name to the request.
5846      */
5847     nocache : true,
5848
5849     /**
5850      * Load data from the configured URL, read the data object into
5851      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5852      * process that block using the passed callback.
5853      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5854      * for the request to the remote server.
5855      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5856      * object into a block of Roo.data.Records.
5857      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5858      * The function must be passed <ul>
5859      * <li>The Record block object</li>
5860      * <li>The "arg" argument from the load function</li>
5861      * <li>A boolean success indicator</li>
5862      * </ul>
5863      * @param {Object} scope The scope in which to call the callback
5864      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5865      */
5866     load : function(params, reader, callback, scope, arg){
5867         if(this.fireEvent("beforeload", this, params) !== false){
5868
5869             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5870
5871             var url = this.url;
5872             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5873             if(this.nocache){
5874                 url += "&_dc=" + (new Date().getTime());
5875             }
5876             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5877             var trans = {
5878                 id : transId,
5879                 cb : "stcCallback"+transId,
5880                 scriptId : "stcScript"+transId,
5881                 params : params,
5882                 arg : arg,
5883                 url : url,
5884                 callback : callback,
5885                 scope : scope,
5886                 reader : reader
5887             };
5888             var conn = this;
5889
5890             window[trans.cb] = function(o){
5891                 conn.handleResponse(o, trans);
5892             };
5893
5894             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5895
5896             if(this.autoAbort !== false){
5897                 this.abort();
5898             }
5899
5900             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5901
5902             var script = document.createElement("script");
5903             script.setAttribute("src", url);
5904             script.setAttribute("type", "text/javascript");
5905             script.setAttribute("id", trans.scriptId);
5906             this.head.appendChild(script);
5907
5908             this.trans = trans;
5909         }else{
5910             callback.call(scope||this, null, arg, false);
5911         }
5912     },
5913
5914     // private
5915     isLoading : function(){
5916         return this.trans ? true : false;
5917     },
5918
5919     /**
5920      * Abort the current server request.
5921      */
5922     abort : function(){
5923         if(this.isLoading()){
5924             this.destroyTrans(this.trans);
5925         }
5926     },
5927
5928     // private
5929     destroyTrans : function(trans, isLoaded){
5930         this.head.removeChild(document.getElementById(trans.scriptId));
5931         clearTimeout(trans.timeoutId);
5932         if(isLoaded){
5933             window[trans.cb] = undefined;
5934             try{
5935                 delete window[trans.cb];
5936             }catch(e){}
5937         }else{
5938             // if hasn't been loaded, wait for load to remove it to prevent script error
5939             window[trans.cb] = function(){
5940                 window[trans.cb] = undefined;
5941                 try{
5942                     delete window[trans.cb];
5943                 }catch(e){}
5944             };
5945         }
5946     },
5947
5948     // private
5949     handleResponse : function(o, trans){
5950         this.trans = false;
5951         this.destroyTrans(trans, true);
5952         var result;
5953         try {
5954             result = trans.reader.readRecords(o);
5955         }catch(e){
5956             this.fireEvent("loadexception", this, o, trans.arg, e);
5957             trans.callback.call(trans.scope||window, null, trans.arg, false);
5958             return;
5959         }
5960         this.fireEvent("load", this, o, trans.arg);
5961         trans.callback.call(trans.scope||window, result, trans.arg, true);
5962     },
5963
5964     // private
5965     handleFailure : function(trans){
5966         this.trans = false;
5967         this.destroyTrans(trans, false);
5968         this.fireEvent("loadexception", this, null, trans.arg);
5969         trans.callback.call(trans.scope||window, null, trans.arg, false);
5970     }
5971 });/*
5972  * Based on:
5973  * Ext JS Library 1.1.1
5974  * Copyright(c) 2006-2007, Ext JS, LLC.
5975  *
5976  * Originally Released Under LGPL - original licence link has changed is not relivant.
5977  *
5978  * Fork - LGPL
5979  * <script type="text/javascript">
5980  */
5981
5982 /**
5983  * @class Roo.data.JsonReader
5984  * @extends Roo.data.DataReader
5985  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
5986  * based on mappings in a provided Roo.data.Record constructor.
5987  * 
5988  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
5989  * in the reply previously. 
5990  * 
5991  * <p>
5992  * Example code:
5993  * <pre><code>
5994 var RecordDef = Roo.data.Record.create([
5995     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
5996     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
5997 ]);
5998 var myReader = new Roo.data.JsonReader({
5999     totalProperty: "results",    // The property which contains the total dataset size (optional)
6000     root: "rows",                // The property which contains an Array of row objects
6001     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6002 }, RecordDef);
6003 </code></pre>
6004  * <p>
6005  * This would consume a JSON file like this:
6006  * <pre><code>
6007 { 'results': 2, 'rows': [
6008     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6009     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6010 }
6011 </code></pre>
6012  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6013  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6014  * paged from the remote server.
6015  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6016  * @cfg {String} root name of the property which contains the Array of row objects.
6017  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6018  * @constructor
6019  * Create a new JsonReader
6020  * @param {Object} meta Metadata configuration options
6021  * @param {Object} recordType Either an Array of field definition objects,
6022  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6023  */
6024 Roo.data.JsonReader = function(meta, recordType){
6025     
6026     meta = meta || {};
6027     // set some defaults:
6028     Roo.applyIf(meta, {
6029         totalProperty: 'total',
6030         successProperty : 'success',
6031         root : 'data',
6032         id : 'id'
6033     });
6034     
6035     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6036 };
6037 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6038     
6039     /**
6040      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6041      * Used by Store query builder to append _requestMeta to params.
6042      * 
6043      */
6044     metaFromRemote : false,
6045     /**
6046      * This method is only used by a DataProxy which has retrieved data from a remote server.
6047      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6048      * @return {Object} data A data block which is used by an Roo.data.Store object as
6049      * a cache of Roo.data.Records.
6050      */
6051     read : function(response){
6052         var json = response.responseText;
6053        
6054         var o = /* eval:var:o */ eval("("+json+")");
6055         if(!o) {
6056             throw {message: "JsonReader.read: Json object not found"};
6057         }
6058         
6059         if(o.metaData){
6060             
6061             delete this.ef;
6062             this.metaFromRemote = true;
6063             this.meta = o.metaData;
6064             this.recordType = Roo.data.Record.create(o.metaData.fields);
6065             this.onMetaChange(this.meta, this.recordType, o);
6066         }
6067         return this.readRecords(o);
6068     },
6069
6070     // private function a store will implement
6071     onMetaChange : function(meta, recordType, o){
6072
6073     },
6074
6075     /**
6076          * @ignore
6077          */
6078     simpleAccess: function(obj, subsc) {
6079         return obj[subsc];
6080     },
6081
6082         /**
6083          * @ignore
6084          */
6085     getJsonAccessor: function(){
6086         var re = /[\[\.]/;
6087         return function(expr) {
6088             try {
6089                 return(re.test(expr))
6090                     ? new Function("obj", "return obj." + expr)
6091                     : function(obj){
6092                         return obj[expr];
6093                     };
6094             } catch(e){}
6095             return Roo.emptyFn;
6096         };
6097     }(),
6098
6099     /**
6100      * Create a data block containing Roo.data.Records from an XML document.
6101      * @param {Object} o An object which contains an Array of row objects in the property specified
6102      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6103      * which contains the total size of the dataset.
6104      * @return {Object} data A data block which is used by an Roo.data.Store object as
6105      * a cache of Roo.data.Records.
6106      */
6107     readRecords : function(o){
6108         /**
6109          * After any data loads, the raw JSON data is available for further custom processing.
6110          * @type Object
6111          */
6112         this.jsonData = o;
6113         var s = this.meta, Record = this.recordType,
6114             f = Record.prototype.fields, fi = f.items, fl = f.length;
6115
6116 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6117         if (!this.ef) {
6118             if(s.totalProperty) {
6119                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6120                 }
6121                 if(s.successProperty) {
6122                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6123                 }
6124                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6125                 if (s.id) {
6126                         var g = this.getJsonAccessor(s.id);
6127                         this.getId = function(rec) {
6128                                 var r = g(rec);
6129                                 return (r === undefined || r === "") ? null : r;
6130                         };
6131                 } else {
6132                         this.getId = function(){return null;};
6133                 }
6134             this.ef = [];
6135             for(var jj = 0; jj < fl; jj++){
6136                 f = fi[jj];
6137                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6138                 this.ef[jj] = this.getJsonAccessor(map);
6139             }
6140         }
6141
6142         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6143         if(s.totalProperty){
6144             var vt = parseInt(this.getTotal(o), 10);
6145             if(!isNaN(vt)){
6146                 totalRecords = vt;
6147             }
6148         }
6149         if(s.successProperty){
6150             var vs = this.getSuccess(o);
6151             if(vs === false || vs === 'false'){
6152                 success = false;
6153             }
6154         }
6155         var records = [];
6156             for(var i = 0; i < c; i++){
6157                     var n = root[i];
6158                 var values = {};
6159                 var id = this.getId(n);
6160                 for(var j = 0; j < fl; j++){
6161                     f = fi[j];
6162                 var v = this.ef[j](n);
6163                 if (!f.convert) {
6164                     Roo.log('missing convert for ' + f.name);
6165                     Roo.log(f);
6166                     continue;
6167                 }
6168                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6169                 }
6170                 var record = new Record(values, id);
6171                 record.json = n;
6172                 records[i] = record;
6173             }
6174             return {
6175                 success : success,
6176                 records : records,
6177                 totalRecords : totalRecords
6178             };
6179     }
6180 });/*
6181  * Based on:
6182  * Ext JS Library 1.1.1
6183  * Copyright(c) 2006-2007, Ext JS, LLC.
6184  *
6185  * Originally Released Under LGPL - original licence link has changed is not relivant.
6186  *
6187  * Fork - LGPL
6188  * <script type="text/javascript">
6189  */
6190
6191 /**
6192  * @class Roo.data.XmlReader
6193  * @extends Roo.data.DataReader
6194  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6195  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6196  * <p>
6197  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6198  * header in the HTTP response must be set to "text/xml".</em>
6199  * <p>
6200  * Example code:
6201  * <pre><code>
6202 var RecordDef = Roo.data.Record.create([
6203    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6204    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6205 ]);
6206 var myReader = new Roo.data.XmlReader({
6207    totalRecords: "results", // The element which contains the total dataset size (optional)
6208    record: "row",           // The repeated element which contains row information
6209    id: "id"                 // The element within the row that provides an ID for the record (optional)
6210 }, RecordDef);
6211 </code></pre>
6212  * <p>
6213  * This would consume an XML file like this:
6214  * <pre><code>
6215 &lt;?xml?>
6216 &lt;dataset>
6217  &lt;results>2&lt;/results>
6218  &lt;row>
6219    &lt;id>1&lt;/id>
6220    &lt;name>Bill&lt;/name>
6221    &lt;occupation>Gardener&lt;/occupation>
6222  &lt;/row>
6223  &lt;row>
6224    &lt;id>2&lt;/id>
6225    &lt;name>Ben&lt;/name>
6226    &lt;occupation>Horticulturalist&lt;/occupation>
6227  &lt;/row>
6228 &lt;/dataset>
6229 </code></pre>
6230  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6231  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6232  * paged from the remote server.
6233  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6234  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6235  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6236  * a record identifier value.
6237  * @constructor
6238  * Create a new XmlReader
6239  * @param {Object} meta Metadata configuration options
6240  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6241  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6242  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6243  */
6244 Roo.data.XmlReader = function(meta, recordType){
6245     meta = meta || {};
6246     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6247 };
6248 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6249     /**
6250      * This method is only used by a DataProxy which has retrieved data from a remote server.
6251          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6252          * to contain a method called 'responseXML' that returns an XML document object.
6253      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6254      * a cache of Roo.data.Records.
6255      */
6256     read : function(response){
6257         var doc = response.responseXML;
6258         if(!doc) {
6259             throw {message: "XmlReader.read: XML Document not available"};
6260         }
6261         return this.readRecords(doc);
6262     },
6263
6264     /**
6265      * Create a data block containing Roo.data.Records from an XML document.
6266          * @param {Object} doc A parsed XML document.
6267      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6268      * a cache of Roo.data.Records.
6269      */
6270     readRecords : function(doc){
6271         /**
6272          * After any data loads/reads, the raw XML Document is available for further custom processing.
6273          * @type XMLDocument
6274          */
6275         this.xmlData = doc;
6276         var root = doc.documentElement || doc;
6277         var q = Roo.DomQuery;
6278         var recordType = this.recordType, fields = recordType.prototype.fields;
6279         var sid = this.meta.id;
6280         var totalRecords = 0, success = true;
6281         if(this.meta.totalRecords){
6282             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6283         }
6284         
6285         if(this.meta.success){
6286             var sv = q.selectValue(this.meta.success, root, true);
6287             success = sv !== false && sv !== 'false';
6288         }
6289         var records = [];
6290         var ns = q.select(this.meta.record, root);
6291         for(var i = 0, len = ns.length; i < len; i++) {
6292                 var n = ns[i];
6293                 var values = {};
6294                 var id = sid ? q.selectValue(sid, n) : undefined;
6295                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6296                     var f = fields.items[j];
6297                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6298                     v = f.convert(v);
6299                     values[f.name] = v;
6300                 }
6301                 var record = new recordType(values, id);
6302                 record.node = n;
6303                 records[records.length] = record;
6304             }
6305
6306             return {
6307                 success : success,
6308                 records : records,
6309                 totalRecords : totalRecords || records.length
6310             };
6311     }
6312 });/*
6313  * Based on:
6314  * Ext JS Library 1.1.1
6315  * Copyright(c) 2006-2007, Ext JS, LLC.
6316  *
6317  * Originally Released Under LGPL - original licence link has changed is not relivant.
6318  *
6319  * Fork - LGPL
6320  * <script type="text/javascript">
6321  */
6322
6323 /**
6324  * @class Roo.data.ArrayReader
6325  * @extends Roo.data.DataReader
6326  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6327  * Each element of that Array represents a row of data fields. The
6328  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6329  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6330  * <p>
6331  * Example code:.
6332  * <pre><code>
6333 var RecordDef = Roo.data.Record.create([
6334     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6335     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6336 ]);
6337 var myReader = new Roo.data.ArrayReader({
6338     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6339 }, RecordDef);
6340 </code></pre>
6341  * <p>
6342  * This would consume an Array like this:
6343  * <pre><code>
6344 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6345   </code></pre>
6346  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6347  * @constructor
6348  * Create a new JsonReader
6349  * @param {Object} meta Metadata configuration options.
6350  * @param {Object} recordType Either an Array of field definition objects
6351  * as specified to {@link Roo.data.Record#create},
6352  * or an {@link Roo.data.Record} object
6353  * created using {@link Roo.data.Record#create}.
6354  */
6355 Roo.data.ArrayReader = function(meta, recordType){
6356     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6357 };
6358
6359 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6360     /**
6361      * Create a data block containing Roo.data.Records from an XML document.
6362      * @param {Object} o An Array of row objects which represents the dataset.
6363      * @return {Object} data A data block which is used by an Roo.data.Store object as
6364      * a cache of Roo.data.Records.
6365      */
6366     readRecords : function(o){
6367         var sid = this.meta ? this.meta.id : null;
6368         var recordType = this.recordType, fields = recordType.prototype.fields;
6369         var records = [];
6370         var root = o;
6371             for(var i = 0; i < root.length; i++){
6372                     var n = root[i];
6373                 var values = {};
6374                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6375                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6376                 var f = fields.items[j];
6377                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6378                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6379                 v = f.convert(v);
6380                 values[f.name] = v;
6381             }
6382                 var record = new recordType(values, id);
6383                 record.json = n;
6384                 records[records.length] = record;
6385             }
6386             return {
6387                 records : records,
6388                 totalRecords : records.length
6389             };
6390     }
6391 });/*
6392  * Based on:
6393  * Ext JS Library 1.1.1
6394  * Copyright(c) 2006-2007, Ext JS, LLC.
6395  *
6396  * Originally Released Under LGPL - original licence link has changed is not relivant.
6397  *
6398  * Fork - LGPL
6399  * <script type="text/javascript">
6400  */
6401
6402
6403 /**
6404  * @class Roo.data.Tree
6405  * @extends Roo.util.Observable
6406  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6407  * in the tree have most standard DOM functionality.
6408  * @constructor
6409  * @param {Node} root (optional) The root node
6410  */
6411 Roo.data.Tree = function(root){
6412    this.nodeHash = {};
6413    /**
6414     * The root node for this tree
6415     * @type Node
6416     */
6417    this.root = null;
6418    if(root){
6419        this.setRootNode(root);
6420    }
6421    this.addEvents({
6422        /**
6423         * @event append
6424         * Fires when a new child node is appended to a node in this tree.
6425         * @param {Tree} tree The owner tree
6426         * @param {Node} parent The parent node
6427         * @param {Node} node The newly appended node
6428         * @param {Number} index The index of the newly appended node
6429         */
6430        "append" : true,
6431        /**
6432         * @event remove
6433         * Fires when a child node is removed from a node in this tree.
6434         * @param {Tree} tree The owner tree
6435         * @param {Node} parent The parent node
6436         * @param {Node} node The child node removed
6437         */
6438        "remove" : true,
6439        /**
6440         * @event move
6441         * Fires when a node is moved to a new location in the tree
6442         * @param {Tree} tree The owner tree
6443         * @param {Node} node The node moved
6444         * @param {Node} oldParent The old parent of this node
6445         * @param {Node} newParent The new parent of this node
6446         * @param {Number} index The index it was moved to
6447         */
6448        "move" : true,
6449        /**
6450         * @event insert
6451         * Fires when a new child node is inserted in a node in this tree.
6452         * @param {Tree} tree The owner tree
6453         * @param {Node} parent The parent node
6454         * @param {Node} node The child node inserted
6455         * @param {Node} refNode The child node the node was inserted before
6456         */
6457        "insert" : true,
6458        /**
6459         * @event beforeappend
6460         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6461         * @param {Tree} tree The owner tree
6462         * @param {Node} parent The parent node
6463         * @param {Node} node The child node to be appended
6464         */
6465        "beforeappend" : true,
6466        /**
6467         * @event beforeremove
6468         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6469         * @param {Tree} tree The owner tree
6470         * @param {Node} parent The parent node
6471         * @param {Node} node The child node to be removed
6472         */
6473        "beforeremove" : true,
6474        /**
6475         * @event beforemove
6476         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6477         * @param {Tree} tree The owner tree
6478         * @param {Node} node The node being moved
6479         * @param {Node} oldParent The parent of the node
6480         * @param {Node} newParent The new parent the node is moving to
6481         * @param {Number} index The index it is being moved to
6482         */
6483        "beforemove" : true,
6484        /**
6485         * @event beforeinsert
6486         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6487         * @param {Tree} tree The owner tree
6488         * @param {Node} parent The parent node
6489         * @param {Node} node The child node to be inserted
6490         * @param {Node} refNode The child node the node is being inserted before
6491         */
6492        "beforeinsert" : true
6493    });
6494
6495     Roo.data.Tree.superclass.constructor.call(this);
6496 };
6497
6498 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6499     pathSeparator: "/",
6500
6501     proxyNodeEvent : function(){
6502         return this.fireEvent.apply(this, arguments);
6503     },
6504
6505     /**
6506      * Returns the root node for this tree.
6507      * @return {Node}
6508      */
6509     getRootNode : function(){
6510         return this.root;
6511     },
6512
6513     /**
6514      * Sets the root node for this tree.
6515      * @param {Node} node
6516      * @return {Node}
6517      */
6518     setRootNode : function(node){
6519         this.root = node;
6520         node.ownerTree = this;
6521         node.isRoot = true;
6522         this.registerNode(node);
6523         return node;
6524     },
6525
6526     /**
6527      * Gets a node in this tree by its id.
6528      * @param {String} id
6529      * @return {Node}
6530      */
6531     getNodeById : function(id){
6532         return this.nodeHash[id];
6533     },
6534
6535     registerNode : function(node){
6536         this.nodeHash[node.id] = node;
6537     },
6538
6539     unregisterNode : function(node){
6540         delete this.nodeHash[node.id];
6541     },
6542
6543     toString : function(){
6544         return "[Tree"+(this.id?" "+this.id:"")+"]";
6545     }
6546 });
6547
6548 /**
6549  * @class Roo.data.Node
6550  * @extends Roo.util.Observable
6551  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6552  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6553  * @constructor
6554  * @param {Object} attributes The attributes/config for the node
6555  */
6556 Roo.data.Node = function(attributes){
6557     /**
6558      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6559      * @type {Object}
6560      */
6561     this.attributes = attributes || {};
6562     this.leaf = this.attributes.leaf;
6563     /**
6564      * The node id. @type String
6565      */
6566     this.id = this.attributes.id;
6567     if(!this.id){
6568         this.id = Roo.id(null, "ynode-");
6569         this.attributes.id = this.id;
6570     }
6571     /**
6572      * All child nodes of this node. @type Array
6573      */
6574     this.childNodes = [];
6575     if(!this.childNodes.indexOf){ // indexOf is a must
6576         this.childNodes.indexOf = function(o){
6577             for(var i = 0, len = this.length; i < len; i++){
6578                 if(this[i] == o) {
6579                     return i;
6580                 }
6581             }
6582             return -1;
6583         };
6584     }
6585     /**
6586      * The parent node for this node. @type Node
6587      */
6588     this.parentNode = null;
6589     /**
6590      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6591      */
6592     this.firstChild = null;
6593     /**
6594      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6595      */
6596     this.lastChild = null;
6597     /**
6598      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6599      */
6600     this.previousSibling = null;
6601     /**
6602      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6603      */
6604     this.nextSibling = null;
6605
6606     this.addEvents({
6607        /**
6608         * @event append
6609         * Fires when a new child node is appended
6610         * @param {Tree} tree The owner tree
6611         * @param {Node} this This node
6612         * @param {Node} node The newly appended node
6613         * @param {Number} index The index of the newly appended node
6614         */
6615        "append" : true,
6616        /**
6617         * @event remove
6618         * Fires when a child node is removed
6619         * @param {Tree} tree The owner tree
6620         * @param {Node} this This node
6621         * @param {Node} node The removed node
6622         */
6623        "remove" : true,
6624        /**
6625         * @event move
6626         * Fires when this node is moved to a new location in the tree
6627         * @param {Tree} tree The owner tree
6628         * @param {Node} this This node
6629         * @param {Node} oldParent The old parent of this node
6630         * @param {Node} newParent The new parent of this node
6631         * @param {Number} index The index it was moved to
6632         */
6633        "move" : true,
6634        /**
6635         * @event insert
6636         * Fires when a new child node is inserted.
6637         * @param {Tree} tree The owner tree
6638         * @param {Node} this This node
6639         * @param {Node} node The child node inserted
6640         * @param {Node} refNode The child node the node was inserted before
6641         */
6642        "insert" : true,
6643        /**
6644         * @event beforeappend
6645         * Fires before a new child is appended, return false to cancel the append.
6646         * @param {Tree} tree The owner tree
6647         * @param {Node} this This node
6648         * @param {Node} node The child node to be appended
6649         */
6650        "beforeappend" : true,
6651        /**
6652         * @event beforeremove
6653         * Fires before a child is removed, return false to cancel the remove.
6654         * @param {Tree} tree The owner tree
6655         * @param {Node} this This node
6656         * @param {Node} node The child node to be removed
6657         */
6658        "beforeremove" : true,
6659        /**
6660         * @event beforemove
6661         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6662         * @param {Tree} tree The owner tree
6663         * @param {Node} this This node
6664         * @param {Node} oldParent The parent of this node
6665         * @param {Node} newParent The new parent this node is moving to
6666         * @param {Number} index The index it is being moved to
6667         */
6668        "beforemove" : true,
6669        /**
6670         * @event beforeinsert
6671         * Fires before a new child is inserted, return false to cancel the insert.
6672         * @param {Tree} tree The owner tree
6673         * @param {Node} this This node
6674         * @param {Node} node The child node to be inserted
6675         * @param {Node} refNode The child node the node is being inserted before
6676         */
6677        "beforeinsert" : true
6678    });
6679     this.listeners = this.attributes.listeners;
6680     Roo.data.Node.superclass.constructor.call(this);
6681 };
6682
6683 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6684     fireEvent : function(evtName){
6685         // first do standard event for this node
6686         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6687             return false;
6688         }
6689         // then bubble it up to the tree if the event wasn't cancelled
6690         var ot = this.getOwnerTree();
6691         if(ot){
6692             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6693                 return false;
6694             }
6695         }
6696         return true;
6697     },
6698
6699     /**
6700      * Returns true if this node is a leaf
6701      * @return {Boolean}
6702      */
6703     isLeaf : function(){
6704         return this.leaf === true;
6705     },
6706
6707     // private
6708     setFirstChild : function(node){
6709         this.firstChild = node;
6710     },
6711
6712     //private
6713     setLastChild : function(node){
6714         this.lastChild = node;
6715     },
6716
6717
6718     /**
6719      * Returns true if this node is the last child of its parent
6720      * @return {Boolean}
6721      */
6722     isLast : function(){
6723        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6724     },
6725
6726     /**
6727      * Returns true if this node is the first child of its parent
6728      * @return {Boolean}
6729      */
6730     isFirst : function(){
6731        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6732     },
6733
6734     hasChildNodes : function(){
6735         return !this.isLeaf() && this.childNodes.length > 0;
6736     },
6737
6738     /**
6739      * Insert node(s) as the last child node of this node.
6740      * @param {Node/Array} node The node or Array of nodes to append
6741      * @return {Node} The appended node if single append, or null if an array was passed
6742      */
6743     appendChild : function(node){
6744         var multi = false;
6745         if(node instanceof Array){
6746             multi = node;
6747         }else if(arguments.length > 1){
6748             multi = arguments;
6749         }
6750         // if passed an array or multiple args do them one by one
6751         if(multi){
6752             for(var i = 0, len = multi.length; i < len; i++) {
6753                 this.appendChild(multi[i]);
6754             }
6755         }else{
6756             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6757                 return false;
6758             }
6759             var index = this.childNodes.length;
6760             var oldParent = node.parentNode;
6761             // it's a move, make sure we move it cleanly
6762             if(oldParent){
6763                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6764                     return false;
6765                 }
6766                 oldParent.removeChild(node);
6767             }
6768             index = this.childNodes.length;
6769             if(index == 0){
6770                 this.setFirstChild(node);
6771             }
6772             this.childNodes.push(node);
6773             node.parentNode = this;
6774             var ps = this.childNodes[index-1];
6775             if(ps){
6776                 node.previousSibling = ps;
6777                 ps.nextSibling = node;
6778             }else{
6779                 node.previousSibling = null;
6780             }
6781             node.nextSibling = null;
6782             this.setLastChild(node);
6783             node.setOwnerTree(this.getOwnerTree());
6784             this.fireEvent("append", this.ownerTree, this, node, index);
6785             if(oldParent){
6786                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6787             }
6788             return node;
6789         }
6790     },
6791
6792     /**
6793      * Removes a child node from this node.
6794      * @param {Node} node The node to remove
6795      * @return {Node} The removed node
6796      */
6797     removeChild : function(node){
6798         var index = this.childNodes.indexOf(node);
6799         if(index == -1){
6800             return false;
6801         }
6802         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6803             return false;
6804         }
6805
6806         // remove it from childNodes collection
6807         this.childNodes.splice(index, 1);
6808
6809         // update siblings
6810         if(node.previousSibling){
6811             node.previousSibling.nextSibling = node.nextSibling;
6812         }
6813         if(node.nextSibling){
6814             node.nextSibling.previousSibling = node.previousSibling;
6815         }
6816
6817         // update child refs
6818         if(this.firstChild == node){
6819             this.setFirstChild(node.nextSibling);
6820         }
6821         if(this.lastChild == node){
6822             this.setLastChild(node.previousSibling);
6823         }
6824
6825         node.setOwnerTree(null);
6826         // clear any references from the node
6827         node.parentNode = null;
6828         node.previousSibling = null;
6829         node.nextSibling = null;
6830         this.fireEvent("remove", this.ownerTree, this, node);
6831         return node;
6832     },
6833
6834     /**
6835      * Inserts the first node before the second node in this nodes childNodes collection.
6836      * @param {Node} node The node to insert
6837      * @param {Node} refNode The node to insert before (if null the node is appended)
6838      * @return {Node} The inserted node
6839      */
6840     insertBefore : function(node, refNode){
6841         if(!refNode){ // like standard Dom, refNode can be null for append
6842             return this.appendChild(node);
6843         }
6844         // nothing to do
6845         if(node == refNode){
6846             return false;
6847         }
6848
6849         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6850             return false;
6851         }
6852         var index = this.childNodes.indexOf(refNode);
6853         var oldParent = node.parentNode;
6854         var refIndex = index;
6855
6856         // when moving internally, indexes will change after remove
6857         if(oldParent == this && this.childNodes.indexOf(node) < index){
6858             refIndex--;
6859         }
6860
6861         // it's a move, make sure we move it cleanly
6862         if(oldParent){
6863             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6864                 return false;
6865             }
6866             oldParent.removeChild(node);
6867         }
6868         if(refIndex == 0){
6869             this.setFirstChild(node);
6870         }
6871         this.childNodes.splice(refIndex, 0, node);
6872         node.parentNode = this;
6873         var ps = this.childNodes[refIndex-1];
6874         if(ps){
6875             node.previousSibling = ps;
6876             ps.nextSibling = node;
6877         }else{
6878             node.previousSibling = null;
6879         }
6880         node.nextSibling = refNode;
6881         refNode.previousSibling = node;
6882         node.setOwnerTree(this.getOwnerTree());
6883         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6884         if(oldParent){
6885             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6886         }
6887         return node;
6888     },
6889
6890     /**
6891      * Returns the child node at the specified index.
6892      * @param {Number} index
6893      * @return {Node}
6894      */
6895     item : function(index){
6896         return this.childNodes[index];
6897     },
6898
6899     /**
6900      * Replaces one child node in this node with another.
6901      * @param {Node} newChild The replacement node
6902      * @param {Node} oldChild The node to replace
6903      * @return {Node} The replaced node
6904      */
6905     replaceChild : function(newChild, oldChild){
6906         this.insertBefore(newChild, oldChild);
6907         this.removeChild(oldChild);
6908         return oldChild;
6909     },
6910
6911     /**
6912      * Returns the index of a child node
6913      * @param {Node} node
6914      * @return {Number} The index of the node or -1 if it was not found
6915      */
6916     indexOf : function(child){
6917         return this.childNodes.indexOf(child);
6918     },
6919
6920     /**
6921      * Returns the tree this node is in.
6922      * @return {Tree}
6923      */
6924     getOwnerTree : function(){
6925         // if it doesn't have one, look for one
6926         if(!this.ownerTree){
6927             var p = this;
6928             while(p){
6929                 if(p.ownerTree){
6930                     this.ownerTree = p.ownerTree;
6931                     break;
6932                 }
6933                 p = p.parentNode;
6934             }
6935         }
6936         return this.ownerTree;
6937     },
6938
6939     /**
6940      * Returns depth of this node (the root node has a depth of 0)
6941      * @return {Number}
6942      */
6943     getDepth : function(){
6944         var depth = 0;
6945         var p = this;
6946         while(p.parentNode){
6947             ++depth;
6948             p = p.parentNode;
6949         }
6950         return depth;
6951     },
6952
6953     // private
6954     setOwnerTree : function(tree){
6955         // if it's move, we need to update everyone
6956         if(tree != this.ownerTree){
6957             if(this.ownerTree){
6958                 this.ownerTree.unregisterNode(this);
6959             }
6960             this.ownerTree = tree;
6961             var cs = this.childNodes;
6962             for(var i = 0, len = cs.length; i < len; i++) {
6963                 cs[i].setOwnerTree(tree);
6964             }
6965             if(tree){
6966                 tree.registerNode(this);
6967             }
6968         }
6969     },
6970
6971     /**
6972      * Returns the path for this node. The path can be used to expand or select this node programmatically.
6973      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
6974      * @return {String} The path
6975      */
6976     getPath : function(attr){
6977         attr = attr || "id";
6978         var p = this.parentNode;
6979         var b = [this.attributes[attr]];
6980         while(p){
6981             b.unshift(p.attributes[attr]);
6982             p = p.parentNode;
6983         }
6984         var sep = this.getOwnerTree().pathSeparator;
6985         return sep + b.join(sep);
6986     },
6987
6988     /**
6989      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
6990      * function call will be the scope provided or the current node. The arguments to the function
6991      * will be the args provided or the current node. If the function returns false at any point,
6992      * the bubble is stopped.
6993      * @param {Function} fn The function to call
6994      * @param {Object} scope (optional) The scope of the function (defaults to current node)
6995      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
6996      */
6997     bubble : function(fn, scope, args){
6998         var p = this;
6999         while(p){
7000             if(fn.call(scope || p, args || p) === false){
7001                 break;
7002             }
7003             p = p.parentNode;
7004         }
7005     },
7006
7007     /**
7008      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7009      * function call will be the scope provided or the current node. The arguments to the function
7010      * will be the args provided or the current node. If the function returns false at any point,
7011      * the cascade is stopped on that branch.
7012      * @param {Function} fn The function to call
7013      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7014      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7015      */
7016     cascade : function(fn, scope, args){
7017         if(fn.call(scope || this, args || this) !== false){
7018             var cs = this.childNodes;
7019             for(var i = 0, len = cs.length; i < len; i++) {
7020                 cs[i].cascade(fn, scope, args);
7021             }
7022         }
7023     },
7024
7025     /**
7026      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7027      * function call will be the scope provided or the current node. The arguments to the function
7028      * will be the args provided or the current node. If the function returns false at any point,
7029      * the iteration stops.
7030      * @param {Function} fn The function to call
7031      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7032      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7033      */
7034     eachChild : function(fn, scope, args){
7035         var cs = this.childNodes;
7036         for(var i = 0, len = cs.length; i < len; i++) {
7037                 if(fn.call(scope || this, args || cs[i]) === false){
7038                     break;
7039                 }
7040         }
7041     },
7042
7043     /**
7044      * Finds the first child that has the attribute with the specified value.
7045      * @param {String} attribute The attribute name
7046      * @param {Mixed} value The value to search for
7047      * @return {Node} The found child or null if none was found
7048      */
7049     findChild : function(attribute, value){
7050         var cs = this.childNodes;
7051         for(var i = 0, len = cs.length; i < len; i++) {
7052                 if(cs[i].attributes[attribute] == value){
7053                     return cs[i];
7054                 }
7055         }
7056         return null;
7057     },
7058
7059     /**
7060      * Finds the first child by a custom function. The child matches if the function passed
7061      * returns true.
7062      * @param {Function} fn
7063      * @param {Object} scope (optional)
7064      * @return {Node} The found child or null if none was found
7065      */
7066     findChildBy : function(fn, scope){
7067         var cs = this.childNodes;
7068         for(var i = 0, len = cs.length; i < len; i++) {
7069                 if(fn.call(scope||cs[i], cs[i]) === true){
7070                     return cs[i];
7071                 }
7072         }
7073         return null;
7074     },
7075
7076     /**
7077      * Sorts this nodes children using the supplied sort function
7078      * @param {Function} fn
7079      * @param {Object} scope (optional)
7080      */
7081     sort : function(fn, scope){
7082         var cs = this.childNodes;
7083         var len = cs.length;
7084         if(len > 0){
7085             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7086             cs.sort(sortFn);
7087             for(var i = 0; i < len; i++){
7088                 var n = cs[i];
7089                 n.previousSibling = cs[i-1];
7090                 n.nextSibling = cs[i+1];
7091                 if(i == 0){
7092                     this.setFirstChild(n);
7093                 }
7094                 if(i == len-1){
7095                     this.setLastChild(n);
7096                 }
7097             }
7098         }
7099     },
7100
7101     /**
7102      * Returns true if this node is an ancestor (at any point) of the passed node.
7103      * @param {Node} node
7104      * @return {Boolean}
7105      */
7106     contains : function(node){
7107         return node.isAncestor(this);
7108     },
7109
7110     /**
7111      * Returns true if the passed node is an ancestor (at any point) of this node.
7112      * @param {Node} node
7113      * @return {Boolean}
7114      */
7115     isAncestor : function(node){
7116         var p = this.parentNode;
7117         while(p){
7118             if(p == node){
7119                 return true;
7120             }
7121             p = p.parentNode;
7122         }
7123         return false;
7124     },
7125
7126     toString : function(){
7127         return "[Node"+(this.id?" "+this.id:"")+"]";
7128     }
7129 });/*
7130  * Based on:
7131  * Ext JS Library 1.1.1
7132  * Copyright(c) 2006-2007, Ext JS, LLC.
7133  *
7134  * Originally Released Under LGPL - original licence link has changed is not relivant.
7135  *
7136  * Fork - LGPL
7137  * <script type="text/javascript">
7138  */
7139  
7140
7141 /**
7142  * @class Roo.ComponentMgr
7143  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7144  * @singleton
7145  */
7146 Roo.ComponentMgr = function(){
7147     var all = new Roo.util.MixedCollection();
7148
7149     return {
7150         /**
7151          * Registers a component.
7152          * @param {Roo.Component} c The component
7153          */
7154         register : function(c){
7155             all.add(c);
7156         },
7157
7158         /**
7159          * Unregisters a component.
7160          * @param {Roo.Component} c The component
7161          */
7162         unregister : function(c){
7163             all.remove(c);
7164         },
7165
7166         /**
7167          * Returns a component by id
7168          * @param {String} id The component id
7169          */
7170         get : function(id){
7171             return all.get(id);
7172         },
7173
7174         /**
7175          * Registers a function that will be called when a specified component is added to ComponentMgr
7176          * @param {String} id The component id
7177          * @param {Funtction} fn The callback function
7178          * @param {Object} scope The scope of the callback
7179          */
7180         onAvailable : function(id, fn, scope){
7181             all.on("add", function(index, o){
7182                 if(o.id == id){
7183                     fn.call(scope || o, o);
7184                     all.un("add", fn, scope);
7185                 }
7186             });
7187         }
7188     };
7189 }();/*
7190  * Based on:
7191  * Ext JS Library 1.1.1
7192  * Copyright(c) 2006-2007, Ext JS, LLC.
7193  *
7194  * Originally Released Under LGPL - original licence link has changed is not relivant.
7195  *
7196  * Fork - LGPL
7197  * <script type="text/javascript">
7198  */
7199  
7200 /**
7201  * @class Roo.Component
7202  * @extends Roo.util.Observable
7203  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7204  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7205  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7206  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7207  * All visual components (widgets) that require rendering into a layout should subclass Component.
7208  * @constructor
7209  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7210  * 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
7211  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7212  */
7213 Roo.Component = function(config){
7214     config = config || {};
7215     if(config.tagName || config.dom || typeof config == "string"){ // element object
7216         config = {el: config, id: config.id || config};
7217     }
7218     this.initialConfig = config;
7219
7220     Roo.apply(this, config);
7221     this.addEvents({
7222         /**
7223          * @event disable
7224          * Fires after the component is disabled.
7225              * @param {Roo.Component} this
7226              */
7227         disable : true,
7228         /**
7229          * @event enable
7230          * Fires after the component is enabled.
7231              * @param {Roo.Component} this
7232              */
7233         enable : true,
7234         /**
7235          * @event beforeshow
7236          * Fires before the component is shown.  Return false to stop the show.
7237              * @param {Roo.Component} this
7238              */
7239         beforeshow : true,
7240         /**
7241          * @event show
7242          * Fires after the component is shown.
7243              * @param {Roo.Component} this
7244              */
7245         show : true,
7246         /**
7247          * @event beforehide
7248          * Fires before the component is hidden. Return false to stop the hide.
7249              * @param {Roo.Component} this
7250              */
7251         beforehide : true,
7252         /**
7253          * @event hide
7254          * Fires after the component is hidden.
7255              * @param {Roo.Component} this
7256              */
7257         hide : true,
7258         /**
7259          * @event beforerender
7260          * Fires before the component is rendered. Return false to stop the render.
7261              * @param {Roo.Component} this
7262              */
7263         beforerender : true,
7264         /**
7265          * @event render
7266          * Fires after the component is rendered.
7267              * @param {Roo.Component} this
7268              */
7269         render : true,
7270         /**
7271          * @event beforedestroy
7272          * Fires before the component is destroyed. Return false to stop the destroy.
7273              * @param {Roo.Component} this
7274              */
7275         beforedestroy : true,
7276         /**
7277          * @event destroy
7278          * Fires after the component is destroyed.
7279              * @param {Roo.Component} this
7280              */
7281         destroy : true
7282     });
7283     if(!this.id){
7284         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7285     }
7286     Roo.ComponentMgr.register(this);
7287     Roo.Component.superclass.constructor.call(this);
7288     this.initComponent();
7289     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7290         this.render(this.renderTo);
7291         delete this.renderTo;
7292     }
7293 };
7294
7295 // private
7296 Roo.Component.AUTO_ID = 1000;
7297
7298 Roo.extend(Roo.Component, Roo.util.Observable, {
7299     /**
7300      * @property {Boolean} hidden
7301      * true if this component is hidden. Read-only.
7302      */
7303     hidden : false,
7304     /**
7305      * true if this component is disabled. Read-only.
7306      */
7307     disabled : false,
7308     /**
7309      * true if this component has been rendered. Read-only.
7310      */
7311     rendered : false,
7312     
7313     /** @cfg {String} disableClass
7314      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7315      */
7316     disabledClass : "x-item-disabled",
7317         /** @cfg {Boolean} allowDomMove
7318          * Whether the component can move the Dom node when rendering (defaults to true).
7319          */
7320     allowDomMove : true,
7321     /** @cfg {String} hideMode
7322      * How this component should hidden. Supported values are
7323      * "visibility" (css visibility), "offsets" (negative offset position) and
7324      * "display" (css display) - defaults to "display".
7325      */
7326     hideMode: 'display',
7327
7328     // private
7329     ctype : "Roo.Component",
7330
7331     /** @cfg {String} actionMode 
7332      * which property holds the element that used for  hide() / show() / disable() / enable()
7333      * default is 'el' 
7334      */
7335     actionMode : "el",
7336
7337     // private
7338     getActionEl : function(){
7339         return this[this.actionMode];
7340     },
7341
7342     initComponent : Roo.emptyFn,
7343     /**
7344      * If this is a lazy rendering component, render it to its container element.
7345      * @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.
7346      */
7347     render : function(container, position){
7348         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7349             if(!container && this.el){
7350                 this.el = Roo.get(this.el);
7351                 container = this.el.dom.parentNode;
7352                 this.allowDomMove = false;
7353             }
7354             this.container = Roo.get(container);
7355             this.rendered = true;
7356             if(position !== undefined){
7357                 if(typeof position == 'number'){
7358                     position = this.container.dom.childNodes[position];
7359                 }else{
7360                     position = Roo.getDom(position);
7361                 }
7362             }
7363             this.onRender(this.container, position || null);
7364             if(this.cls){
7365                 this.el.addClass(this.cls);
7366                 delete this.cls;
7367             }
7368             if(this.style){
7369                 this.el.applyStyles(this.style);
7370                 delete this.style;
7371             }
7372             this.fireEvent("render", this);
7373             this.afterRender(this.container);
7374             if(this.hidden){
7375                 this.hide();
7376             }
7377             if(this.disabled){
7378                 this.disable();
7379             }
7380         }
7381         return this;
7382     },
7383
7384     // private
7385     // default function is not really useful
7386     onRender : function(ct, position){
7387         if(this.el){
7388             this.el = Roo.get(this.el);
7389             if(this.allowDomMove !== false){
7390                 ct.dom.insertBefore(this.el.dom, position);
7391             }
7392         }
7393     },
7394
7395     // private
7396     getAutoCreate : function(){
7397         var cfg = typeof this.autoCreate == "object" ?
7398                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7399         if(this.id && !cfg.id){
7400             cfg.id = this.id;
7401         }
7402         return cfg;
7403     },
7404
7405     // private
7406     afterRender : Roo.emptyFn,
7407
7408     /**
7409      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7410      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7411      */
7412     destroy : function(){
7413         if(this.fireEvent("beforedestroy", this) !== false){
7414             this.purgeListeners();
7415             this.beforeDestroy();
7416             if(this.rendered){
7417                 this.el.removeAllListeners();
7418                 this.el.remove();
7419                 if(this.actionMode == "container"){
7420                     this.container.remove();
7421                 }
7422             }
7423             this.onDestroy();
7424             Roo.ComponentMgr.unregister(this);
7425             this.fireEvent("destroy", this);
7426         }
7427     },
7428
7429         // private
7430     beforeDestroy : function(){
7431
7432     },
7433
7434         // private
7435         onDestroy : function(){
7436
7437     },
7438
7439     /**
7440      * Returns the underlying {@link Roo.Element}.
7441      * @return {Roo.Element} The element
7442      */
7443     getEl : function(){
7444         return this.el;
7445     },
7446
7447     /**
7448      * Returns the id of this component.
7449      * @return {String}
7450      */
7451     getId : function(){
7452         return this.id;
7453     },
7454
7455     /**
7456      * Try to focus this component.
7457      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7458      * @return {Roo.Component} this
7459      */
7460     focus : function(selectText){
7461         if(this.rendered){
7462             this.el.focus();
7463             if(selectText === true){
7464                 this.el.dom.select();
7465             }
7466         }
7467         return this;
7468     },
7469
7470     // private
7471     blur : function(){
7472         if(this.rendered){
7473             this.el.blur();
7474         }
7475         return this;
7476     },
7477
7478     /**
7479      * Disable this component.
7480      * @return {Roo.Component} this
7481      */
7482     disable : function(){
7483         if(this.rendered){
7484             this.onDisable();
7485         }
7486         this.disabled = true;
7487         this.fireEvent("disable", this);
7488         return this;
7489     },
7490
7491         // private
7492     onDisable : function(){
7493         this.getActionEl().addClass(this.disabledClass);
7494         this.el.dom.disabled = true;
7495     },
7496
7497     /**
7498      * Enable this component.
7499      * @return {Roo.Component} this
7500      */
7501     enable : function(){
7502         if(this.rendered){
7503             this.onEnable();
7504         }
7505         this.disabled = false;
7506         this.fireEvent("enable", this);
7507         return this;
7508     },
7509
7510         // private
7511     onEnable : function(){
7512         this.getActionEl().removeClass(this.disabledClass);
7513         this.el.dom.disabled = false;
7514     },
7515
7516     /**
7517      * Convenience function for setting disabled/enabled by boolean.
7518      * @param {Boolean} disabled
7519      */
7520     setDisabled : function(disabled){
7521         this[disabled ? "disable" : "enable"]();
7522     },
7523
7524     /**
7525      * Show this component.
7526      * @return {Roo.Component} this
7527      */
7528     show: function(){
7529         if(this.fireEvent("beforeshow", this) !== false){
7530             this.hidden = false;
7531             if(this.rendered){
7532                 this.onShow();
7533             }
7534             this.fireEvent("show", this);
7535         }
7536         return this;
7537     },
7538
7539     // private
7540     onShow : function(){
7541         var ae = this.getActionEl();
7542         if(this.hideMode == 'visibility'){
7543             ae.dom.style.visibility = "visible";
7544         }else if(this.hideMode == 'offsets'){
7545             ae.removeClass('x-hidden');
7546         }else{
7547             ae.dom.style.display = "";
7548         }
7549     },
7550
7551     /**
7552      * Hide this component.
7553      * @return {Roo.Component} this
7554      */
7555     hide: function(){
7556         if(this.fireEvent("beforehide", this) !== false){
7557             this.hidden = true;
7558             if(this.rendered){
7559                 this.onHide();
7560             }
7561             this.fireEvent("hide", this);
7562         }
7563         return this;
7564     },
7565
7566     // private
7567     onHide : function(){
7568         var ae = this.getActionEl();
7569         if(this.hideMode == 'visibility'){
7570             ae.dom.style.visibility = "hidden";
7571         }else if(this.hideMode == 'offsets'){
7572             ae.addClass('x-hidden');
7573         }else{
7574             ae.dom.style.display = "none";
7575         }
7576     },
7577
7578     /**
7579      * Convenience function to hide or show this component by boolean.
7580      * @param {Boolean} visible True to show, false to hide
7581      * @return {Roo.Component} this
7582      */
7583     setVisible: function(visible){
7584         if(visible) {
7585             this.show();
7586         }else{
7587             this.hide();
7588         }
7589         return this;
7590     },
7591
7592     /**
7593      * Returns true if this component is visible.
7594      */
7595     isVisible : function(){
7596         return this.getActionEl().isVisible();
7597     },
7598
7599     cloneConfig : function(overrides){
7600         overrides = overrides || {};
7601         var id = overrides.id || Roo.id();
7602         var cfg = Roo.applyIf(overrides, this.initialConfig);
7603         cfg.id = id; // prevent dup id
7604         return new this.constructor(cfg);
7605     }
7606 });/*
7607  * Based on:
7608  * Ext JS Library 1.1.1
7609  * Copyright(c) 2006-2007, Ext JS, LLC.
7610  *
7611  * Originally Released Under LGPL - original licence link has changed is not relivant.
7612  *
7613  * Fork - LGPL
7614  * <script type="text/javascript">
7615  */
7616  (function(){ 
7617 /**
7618  * @class Roo.Layer
7619  * @extends Roo.Element
7620  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7621  * automatic maintaining of shadow/shim positions.
7622  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7623  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7624  * you can pass a string with a CSS class name. False turns off the shadow.
7625  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7626  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7627  * @cfg {String} cls CSS class to add to the element
7628  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7629  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7630  * @constructor
7631  * @param {Object} config An object with config options.
7632  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7633  */
7634
7635 Roo.Layer = function(config, existingEl){
7636     config = config || {};
7637     var dh = Roo.DomHelper;
7638     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7639     if(existingEl){
7640         this.dom = Roo.getDom(existingEl);
7641     }
7642     if(!this.dom){
7643         var o = config.dh || {tag: "div", cls: "x-layer"};
7644         this.dom = dh.append(pel, o);
7645     }
7646     if(config.cls){
7647         this.addClass(config.cls);
7648     }
7649     this.constrain = config.constrain !== false;
7650     this.visibilityMode = Roo.Element.VISIBILITY;
7651     if(config.id){
7652         this.id = this.dom.id = config.id;
7653     }else{
7654         this.id = Roo.id(this.dom);
7655     }
7656     this.zindex = config.zindex || this.getZIndex();
7657     this.position("absolute", this.zindex);
7658     if(config.shadow){
7659         this.shadowOffset = config.shadowOffset || 4;
7660         this.shadow = new Roo.Shadow({
7661             offset : this.shadowOffset,
7662             mode : config.shadow
7663         });
7664     }else{
7665         this.shadowOffset = 0;
7666     }
7667     this.useShim = config.shim !== false && Roo.useShims;
7668     this.useDisplay = config.useDisplay;
7669     this.hide();
7670 };
7671
7672 var supr = Roo.Element.prototype;
7673
7674 // shims are shared among layer to keep from having 100 iframes
7675 var shims = [];
7676
7677 Roo.extend(Roo.Layer, Roo.Element, {
7678
7679     getZIndex : function(){
7680         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7681     },
7682
7683     getShim : function(){
7684         if(!this.useShim){
7685             return null;
7686         }
7687         if(this.shim){
7688             return this.shim;
7689         }
7690         var shim = shims.shift();
7691         if(!shim){
7692             shim = this.createShim();
7693             shim.enableDisplayMode('block');
7694             shim.dom.style.display = 'none';
7695             shim.dom.style.visibility = 'visible';
7696         }
7697         var pn = this.dom.parentNode;
7698         if(shim.dom.parentNode != pn){
7699             pn.insertBefore(shim.dom, this.dom);
7700         }
7701         shim.setStyle('z-index', this.getZIndex()-2);
7702         this.shim = shim;
7703         return shim;
7704     },
7705
7706     hideShim : function(){
7707         if(this.shim){
7708             this.shim.setDisplayed(false);
7709             shims.push(this.shim);
7710             delete this.shim;
7711         }
7712     },
7713
7714     disableShadow : function(){
7715         if(this.shadow){
7716             this.shadowDisabled = true;
7717             this.shadow.hide();
7718             this.lastShadowOffset = this.shadowOffset;
7719             this.shadowOffset = 0;
7720         }
7721     },
7722
7723     enableShadow : function(show){
7724         if(this.shadow){
7725             this.shadowDisabled = false;
7726             this.shadowOffset = this.lastShadowOffset;
7727             delete this.lastShadowOffset;
7728             if(show){
7729                 this.sync(true);
7730             }
7731         }
7732     },
7733
7734     // private
7735     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7736     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7737     sync : function(doShow){
7738         var sw = this.shadow;
7739         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7740             var sh = this.getShim();
7741
7742             var w = this.getWidth(),
7743                 h = this.getHeight();
7744
7745             var l = this.getLeft(true),
7746                 t = this.getTop(true);
7747
7748             if(sw && !this.shadowDisabled){
7749                 if(doShow && !sw.isVisible()){
7750                     sw.show(this);
7751                 }else{
7752                     sw.realign(l, t, w, h);
7753                 }
7754                 if(sh){
7755                     if(doShow){
7756                        sh.show();
7757                     }
7758                     // fit the shim behind the shadow, so it is shimmed too
7759                     var a = sw.adjusts, s = sh.dom.style;
7760                     s.left = (Math.min(l, l+a.l))+"px";
7761                     s.top = (Math.min(t, t+a.t))+"px";
7762                     s.width = (w+a.w)+"px";
7763                     s.height = (h+a.h)+"px";
7764                 }
7765             }else if(sh){
7766                 if(doShow){
7767                    sh.show();
7768                 }
7769                 sh.setSize(w, h);
7770                 sh.setLeftTop(l, t);
7771             }
7772             
7773         }
7774     },
7775
7776     // private
7777     destroy : function(){
7778         this.hideShim();
7779         if(this.shadow){
7780             this.shadow.hide();
7781         }
7782         this.removeAllListeners();
7783         var pn = this.dom.parentNode;
7784         if(pn){
7785             pn.removeChild(this.dom);
7786         }
7787         Roo.Element.uncache(this.id);
7788     },
7789
7790     remove : function(){
7791         this.destroy();
7792     },
7793
7794     // private
7795     beginUpdate : function(){
7796         this.updating = true;
7797     },
7798
7799     // private
7800     endUpdate : function(){
7801         this.updating = false;
7802         this.sync(true);
7803     },
7804
7805     // private
7806     hideUnders : function(negOffset){
7807         if(this.shadow){
7808             this.shadow.hide();
7809         }
7810         this.hideShim();
7811     },
7812
7813     // private
7814     constrainXY : function(){
7815         if(this.constrain){
7816             var vw = Roo.lib.Dom.getViewWidth(),
7817                 vh = Roo.lib.Dom.getViewHeight();
7818             var s = Roo.get(document).getScroll();
7819
7820             var xy = this.getXY();
7821             var x = xy[0], y = xy[1];   
7822             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7823             // only move it if it needs it
7824             var moved = false;
7825             // first validate right/bottom
7826             if((x + w) > vw+s.left){
7827                 x = vw - w - this.shadowOffset;
7828                 moved = true;
7829             }
7830             if((y + h) > vh+s.top){
7831                 y = vh - h - this.shadowOffset;
7832                 moved = true;
7833             }
7834             // then make sure top/left isn't negative
7835             if(x < s.left){
7836                 x = s.left;
7837                 moved = true;
7838             }
7839             if(y < s.top){
7840                 y = s.top;
7841                 moved = true;
7842             }
7843             if(moved){
7844                 if(this.avoidY){
7845                     var ay = this.avoidY;
7846                     if(y <= ay && (y+h) >= ay){
7847                         y = ay-h-5;   
7848                     }
7849                 }
7850                 xy = [x, y];
7851                 this.storeXY(xy);
7852                 supr.setXY.call(this, xy);
7853                 this.sync();
7854             }
7855         }
7856     },
7857
7858     isVisible : function(){
7859         return this.visible;    
7860     },
7861
7862     // private
7863     showAction : function(){
7864         this.visible = true; // track visibility to prevent getStyle calls
7865         if(this.useDisplay === true){
7866             this.setDisplayed("");
7867         }else if(this.lastXY){
7868             supr.setXY.call(this, this.lastXY);
7869         }else if(this.lastLT){
7870             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7871         }
7872     },
7873
7874     // private
7875     hideAction : function(){
7876         this.visible = false;
7877         if(this.useDisplay === true){
7878             this.setDisplayed(false);
7879         }else{
7880             this.setLeftTop(-10000,-10000);
7881         }
7882     },
7883
7884     // overridden Element method
7885     setVisible : function(v, a, d, c, e){
7886         if(v){
7887             this.showAction();
7888         }
7889         if(a && v){
7890             var cb = function(){
7891                 this.sync(true);
7892                 if(c){
7893                     c();
7894                 }
7895             }.createDelegate(this);
7896             supr.setVisible.call(this, true, true, d, cb, e);
7897         }else{
7898             if(!v){
7899                 this.hideUnders(true);
7900             }
7901             var cb = c;
7902             if(a){
7903                 cb = function(){
7904                     this.hideAction();
7905                     if(c){
7906                         c();
7907                     }
7908                 }.createDelegate(this);
7909             }
7910             supr.setVisible.call(this, v, a, d, cb, e);
7911             if(v){
7912                 this.sync(true);
7913             }else if(!a){
7914                 this.hideAction();
7915             }
7916         }
7917     },
7918
7919     storeXY : function(xy){
7920         delete this.lastLT;
7921         this.lastXY = xy;
7922     },
7923
7924     storeLeftTop : function(left, top){
7925         delete this.lastXY;
7926         this.lastLT = [left, top];
7927     },
7928
7929     // private
7930     beforeFx : function(){
7931         this.beforeAction();
7932         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
7933     },
7934
7935     // private
7936     afterFx : function(){
7937         Roo.Layer.superclass.afterFx.apply(this, arguments);
7938         this.sync(this.isVisible());
7939     },
7940
7941     // private
7942     beforeAction : function(){
7943         if(!this.updating && this.shadow){
7944             this.shadow.hide();
7945         }
7946     },
7947
7948     // overridden Element method
7949     setLeft : function(left){
7950         this.storeLeftTop(left, this.getTop(true));
7951         supr.setLeft.apply(this, arguments);
7952         this.sync();
7953     },
7954
7955     setTop : function(top){
7956         this.storeLeftTop(this.getLeft(true), top);
7957         supr.setTop.apply(this, arguments);
7958         this.sync();
7959     },
7960
7961     setLeftTop : function(left, top){
7962         this.storeLeftTop(left, top);
7963         supr.setLeftTop.apply(this, arguments);
7964         this.sync();
7965     },
7966
7967     setXY : function(xy, a, d, c, e){
7968         this.fixDisplay();
7969         this.beforeAction();
7970         this.storeXY(xy);
7971         var cb = this.createCB(c);
7972         supr.setXY.call(this, xy, a, d, cb, e);
7973         if(!a){
7974             cb();
7975         }
7976     },
7977
7978     // private
7979     createCB : function(c){
7980         var el = this;
7981         return function(){
7982             el.constrainXY();
7983             el.sync(true);
7984             if(c){
7985                 c();
7986             }
7987         };
7988     },
7989
7990     // overridden Element method
7991     setX : function(x, a, d, c, e){
7992         this.setXY([x, this.getY()], a, d, c, e);
7993     },
7994
7995     // overridden Element method
7996     setY : function(y, a, d, c, e){
7997         this.setXY([this.getX(), y], a, d, c, e);
7998     },
7999
8000     // overridden Element method
8001     setSize : function(w, h, a, d, c, e){
8002         this.beforeAction();
8003         var cb = this.createCB(c);
8004         supr.setSize.call(this, w, h, a, d, cb, e);
8005         if(!a){
8006             cb();
8007         }
8008     },
8009
8010     // overridden Element method
8011     setWidth : function(w, a, d, c, e){
8012         this.beforeAction();
8013         var cb = this.createCB(c);
8014         supr.setWidth.call(this, w, a, d, cb, e);
8015         if(!a){
8016             cb();
8017         }
8018     },
8019
8020     // overridden Element method
8021     setHeight : function(h, a, d, c, e){
8022         this.beforeAction();
8023         var cb = this.createCB(c);
8024         supr.setHeight.call(this, h, a, d, cb, e);
8025         if(!a){
8026             cb();
8027         }
8028     },
8029
8030     // overridden Element method
8031     setBounds : function(x, y, w, h, a, d, c, e){
8032         this.beforeAction();
8033         var cb = this.createCB(c);
8034         if(!a){
8035             this.storeXY([x, y]);
8036             supr.setXY.call(this, [x, y]);
8037             supr.setSize.call(this, w, h, a, d, cb, e);
8038             cb();
8039         }else{
8040             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8041         }
8042         return this;
8043     },
8044     
8045     /**
8046      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8047      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8048      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8049      * @param {Number} zindex The new z-index to set
8050      * @return {this} The Layer
8051      */
8052     setZIndex : function(zindex){
8053         this.zindex = zindex;
8054         this.setStyle("z-index", zindex + 2);
8055         if(this.shadow){
8056             this.shadow.setZIndex(zindex + 1);
8057         }
8058         if(this.shim){
8059             this.shim.setStyle("z-index", zindex);
8060         }
8061     }
8062 });
8063 })();/*
8064  * Based on:
8065  * Ext JS Library 1.1.1
8066  * Copyright(c) 2006-2007, Ext JS, LLC.
8067  *
8068  * Originally Released Under LGPL - original licence link has changed is not relivant.
8069  *
8070  * Fork - LGPL
8071  * <script type="text/javascript">
8072  */
8073
8074
8075 /**
8076  * @class Roo.Shadow
8077  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8078  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8079  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8080  * @constructor
8081  * Create a new Shadow
8082  * @param {Object} config The config object
8083  */
8084 Roo.Shadow = function(config){
8085     Roo.apply(this, config);
8086     if(typeof this.mode != "string"){
8087         this.mode = this.defaultMode;
8088     }
8089     var o = this.offset, a = {h: 0};
8090     var rad = Math.floor(this.offset/2);
8091     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8092         case "drop":
8093             a.w = 0;
8094             a.l = a.t = o;
8095             a.t -= 1;
8096             if(Roo.isIE){
8097                 a.l -= this.offset + rad;
8098                 a.t -= this.offset + rad;
8099                 a.w -= rad;
8100                 a.h -= rad;
8101                 a.t += 1;
8102             }
8103         break;
8104         case "sides":
8105             a.w = (o*2);
8106             a.l = -o;
8107             a.t = o-1;
8108             if(Roo.isIE){
8109                 a.l -= (this.offset - rad);
8110                 a.t -= this.offset + rad;
8111                 a.l += 1;
8112                 a.w -= (this.offset - rad)*2;
8113                 a.w -= rad + 1;
8114                 a.h -= 1;
8115             }
8116         break;
8117         case "frame":
8118             a.w = a.h = (o*2);
8119             a.l = a.t = -o;
8120             a.t += 1;
8121             a.h -= 2;
8122             if(Roo.isIE){
8123                 a.l -= (this.offset - rad);
8124                 a.t -= (this.offset - rad);
8125                 a.l += 1;
8126                 a.w -= (this.offset + rad + 1);
8127                 a.h -= (this.offset + rad);
8128                 a.h += 1;
8129             }
8130         break;
8131     };
8132
8133     this.adjusts = a;
8134 };
8135
8136 Roo.Shadow.prototype = {
8137     /**
8138      * @cfg {String} mode
8139      * The shadow display mode.  Supports the following options:<br />
8140      * sides: Shadow displays on both sides and bottom only<br />
8141      * frame: Shadow displays equally on all four sides<br />
8142      * drop: Traditional bottom-right drop shadow (default)
8143      */
8144     /**
8145      * @cfg {String} offset
8146      * The number of pixels to offset the shadow from the element (defaults to 4)
8147      */
8148     offset: 4,
8149
8150     // private
8151     defaultMode: "drop",
8152
8153     /**
8154      * Displays the shadow under the target element
8155      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8156      */
8157     show : function(target){
8158         target = Roo.get(target);
8159         if(!this.el){
8160             this.el = Roo.Shadow.Pool.pull();
8161             if(this.el.dom.nextSibling != target.dom){
8162                 this.el.insertBefore(target);
8163             }
8164         }
8165         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8166         if(Roo.isIE){
8167             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8168         }
8169         this.realign(
8170             target.getLeft(true),
8171             target.getTop(true),
8172             target.getWidth(),
8173             target.getHeight()
8174         );
8175         this.el.dom.style.display = "block";
8176     },
8177
8178     /**
8179      * Returns true if the shadow is visible, else false
8180      */
8181     isVisible : function(){
8182         return this.el ? true : false;  
8183     },
8184
8185     /**
8186      * Direct alignment when values are already available. Show must be called at least once before
8187      * calling this method to ensure it is initialized.
8188      * @param {Number} left The target element left position
8189      * @param {Number} top The target element top position
8190      * @param {Number} width The target element width
8191      * @param {Number} height The target element height
8192      */
8193     realign : function(l, t, w, h){
8194         if(!this.el){
8195             return;
8196         }
8197         var a = this.adjusts, d = this.el.dom, s = d.style;
8198         var iea = 0;
8199         s.left = (l+a.l)+"px";
8200         s.top = (t+a.t)+"px";
8201         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8202  
8203         if(s.width != sws || s.height != shs){
8204             s.width = sws;
8205             s.height = shs;
8206             if(!Roo.isIE){
8207                 var cn = d.childNodes;
8208                 var sww = Math.max(0, (sw-12))+"px";
8209                 cn[0].childNodes[1].style.width = sww;
8210                 cn[1].childNodes[1].style.width = sww;
8211                 cn[2].childNodes[1].style.width = sww;
8212                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8213             }
8214         }
8215     },
8216
8217     /**
8218      * Hides this shadow
8219      */
8220     hide : function(){
8221         if(this.el){
8222             this.el.dom.style.display = "none";
8223             Roo.Shadow.Pool.push(this.el);
8224             delete this.el;
8225         }
8226     },
8227
8228     /**
8229      * Adjust the z-index of this shadow
8230      * @param {Number} zindex The new z-index
8231      */
8232     setZIndex : function(z){
8233         this.zIndex = z;
8234         if(this.el){
8235             this.el.setStyle("z-index", z);
8236         }
8237     }
8238 };
8239
8240 // Private utility class that manages the internal Shadow cache
8241 Roo.Shadow.Pool = function(){
8242     var p = [];
8243     var markup = Roo.isIE ?
8244                  '<div class="x-ie-shadow"></div>' :
8245                  '<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>';
8246     return {
8247         pull : function(){
8248             var sh = p.shift();
8249             if(!sh){
8250                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8251                 sh.autoBoxAdjust = false;
8252             }
8253             return sh;
8254         },
8255
8256         push : function(sh){
8257             p.push(sh);
8258         }
8259     };
8260 }();/*
8261  * Based on:
8262  * Ext JS Library 1.1.1
8263  * Copyright(c) 2006-2007, Ext JS, LLC.
8264  *
8265  * Originally Released Under LGPL - original licence link has changed is not relivant.
8266  *
8267  * Fork - LGPL
8268  * <script type="text/javascript">
8269  */
8270
8271 /**
8272  * @class Roo.BoxComponent
8273  * @extends Roo.Component
8274  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8275  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8276  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8277  * layout containers.
8278  * @constructor
8279  * @param {Roo.Element/String/Object} config The configuration options.
8280  */
8281 Roo.BoxComponent = function(config){
8282     Roo.Component.call(this, config);
8283     this.addEvents({
8284         /**
8285          * @event resize
8286          * Fires after the component is resized.
8287              * @param {Roo.Component} this
8288              * @param {Number} adjWidth The box-adjusted width that was set
8289              * @param {Number} adjHeight The box-adjusted height that was set
8290              * @param {Number} rawWidth The width that was originally specified
8291              * @param {Number} rawHeight The height that was originally specified
8292              */
8293         resize : true,
8294         /**
8295          * @event move
8296          * Fires after the component is moved.
8297              * @param {Roo.Component} this
8298              * @param {Number} x The new x position
8299              * @param {Number} y The new y position
8300              */
8301         move : true
8302     });
8303 };
8304
8305 Roo.extend(Roo.BoxComponent, Roo.Component, {
8306     // private, set in afterRender to signify that the component has been rendered
8307     boxReady : false,
8308     // private, used to defer height settings to subclasses
8309     deferHeight: false,
8310     /** @cfg {Number} width
8311      * width (optional) size of component
8312      */
8313      /** @cfg {Number} height
8314      * height (optional) size of component
8315      */
8316      
8317     /**
8318      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8319      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8320      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8321      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8322      * @return {Roo.BoxComponent} this
8323      */
8324     setSize : function(w, h){
8325         // support for standard size objects
8326         if(typeof w == 'object'){
8327             h = w.height;
8328             w = w.width;
8329         }
8330         // not rendered
8331         if(!this.boxReady){
8332             this.width = w;
8333             this.height = h;
8334             return this;
8335         }
8336
8337         // prevent recalcs when not needed
8338         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8339             return this;
8340         }
8341         this.lastSize = {width: w, height: h};
8342
8343         var adj = this.adjustSize(w, h);
8344         var aw = adj.width, ah = adj.height;
8345         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8346             var rz = this.getResizeEl();
8347             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8348                 rz.setSize(aw, ah);
8349             }else if(!this.deferHeight && ah !== undefined){
8350                 rz.setHeight(ah);
8351             }else if(aw !== undefined){
8352                 rz.setWidth(aw);
8353             }
8354             this.onResize(aw, ah, w, h);
8355             this.fireEvent('resize', this, aw, ah, w, h);
8356         }
8357         return this;
8358     },
8359
8360     /**
8361      * Gets the current size of the component's underlying element.
8362      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8363      */
8364     getSize : function(){
8365         return this.el.getSize();
8366     },
8367
8368     /**
8369      * Gets the current XY position of the component's underlying element.
8370      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8371      * @return {Array} The XY position of the element (e.g., [100, 200])
8372      */
8373     getPosition : function(local){
8374         if(local === true){
8375             return [this.el.getLeft(true), this.el.getTop(true)];
8376         }
8377         return this.xy || this.el.getXY();
8378     },
8379
8380     /**
8381      * Gets the current box measurements of the component's underlying element.
8382      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8383      * @returns {Object} box An object in the format {x, y, width, height}
8384      */
8385     getBox : function(local){
8386         var s = this.el.getSize();
8387         if(local){
8388             s.x = this.el.getLeft(true);
8389             s.y = this.el.getTop(true);
8390         }else{
8391             var xy = this.xy || this.el.getXY();
8392             s.x = xy[0];
8393             s.y = xy[1];
8394         }
8395         return s;
8396     },
8397
8398     /**
8399      * Sets the current box measurements of the component's underlying element.
8400      * @param {Object} box An object in the format {x, y, width, height}
8401      * @returns {Roo.BoxComponent} this
8402      */
8403     updateBox : function(box){
8404         this.setSize(box.width, box.height);
8405         this.setPagePosition(box.x, box.y);
8406         return this;
8407     },
8408
8409     // protected
8410     getResizeEl : function(){
8411         return this.resizeEl || this.el;
8412     },
8413
8414     // protected
8415     getPositionEl : function(){
8416         return this.positionEl || this.el;
8417     },
8418
8419     /**
8420      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8421      * This method fires the move event.
8422      * @param {Number} left The new left
8423      * @param {Number} top The new top
8424      * @returns {Roo.BoxComponent} this
8425      */
8426     setPosition : function(x, y){
8427         this.x = x;
8428         this.y = y;
8429         if(!this.boxReady){
8430             return this;
8431         }
8432         var adj = this.adjustPosition(x, y);
8433         var ax = adj.x, ay = adj.y;
8434
8435         var el = this.getPositionEl();
8436         if(ax !== undefined || ay !== undefined){
8437             if(ax !== undefined && ay !== undefined){
8438                 el.setLeftTop(ax, ay);
8439             }else if(ax !== undefined){
8440                 el.setLeft(ax);
8441             }else if(ay !== undefined){
8442                 el.setTop(ay);
8443             }
8444             this.onPosition(ax, ay);
8445             this.fireEvent('move', this, ax, ay);
8446         }
8447         return this;
8448     },
8449
8450     /**
8451      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8452      * This method fires the move event.
8453      * @param {Number} x The new x position
8454      * @param {Number} y The new y position
8455      * @returns {Roo.BoxComponent} this
8456      */
8457     setPagePosition : function(x, y){
8458         this.pageX = x;
8459         this.pageY = y;
8460         if(!this.boxReady){
8461             return;
8462         }
8463         if(x === undefined || y === undefined){ // cannot translate undefined points
8464             return;
8465         }
8466         var p = this.el.translatePoints(x, y);
8467         this.setPosition(p.left, p.top);
8468         return this;
8469     },
8470
8471     // private
8472     onRender : function(ct, position){
8473         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8474         if(this.resizeEl){
8475             this.resizeEl = Roo.get(this.resizeEl);
8476         }
8477         if(this.positionEl){
8478             this.positionEl = Roo.get(this.positionEl);
8479         }
8480     },
8481
8482     // private
8483     afterRender : function(){
8484         Roo.BoxComponent.superclass.afterRender.call(this);
8485         this.boxReady = true;
8486         this.setSize(this.width, this.height);
8487         if(this.x || this.y){
8488             this.setPosition(this.x, this.y);
8489         }
8490         if(this.pageX || this.pageY){
8491             this.setPagePosition(this.pageX, this.pageY);
8492         }
8493     },
8494
8495     /**
8496      * Force the component's size to recalculate based on the underlying element's current height and width.
8497      * @returns {Roo.BoxComponent} this
8498      */
8499     syncSize : function(){
8500         delete this.lastSize;
8501         this.setSize(this.el.getWidth(), this.el.getHeight());
8502         return this;
8503     },
8504
8505     /**
8506      * Called after the component is resized, this method is empty by default but can be implemented by any
8507      * subclass that needs to perform custom logic after a resize occurs.
8508      * @param {Number} adjWidth The box-adjusted width that was set
8509      * @param {Number} adjHeight The box-adjusted height that was set
8510      * @param {Number} rawWidth The width that was originally specified
8511      * @param {Number} rawHeight The height that was originally specified
8512      */
8513     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8514
8515     },
8516
8517     /**
8518      * Called after the component is moved, this method is empty by default but can be implemented by any
8519      * subclass that needs to perform custom logic after a move occurs.
8520      * @param {Number} x The new x position
8521      * @param {Number} y The new y position
8522      */
8523     onPosition : function(x, y){
8524
8525     },
8526
8527     // private
8528     adjustSize : function(w, h){
8529         if(this.autoWidth){
8530             w = 'auto';
8531         }
8532         if(this.autoHeight){
8533             h = 'auto';
8534         }
8535         return {width : w, height: h};
8536     },
8537
8538     // private
8539     adjustPosition : function(x, y){
8540         return {x : x, y: y};
8541     }
8542 });/*
8543  * Based on:
8544  * Ext JS Library 1.1.1
8545  * Copyright(c) 2006-2007, Ext JS, LLC.
8546  *
8547  * Originally Released Under LGPL - original licence link has changed is not relivant.
8548  *
8549  * Fork - LGPL
8550  * <script type="text/javascript">
8551  */
8552
8553
8554 /**
8555  * @class Roo.SplitBar
8556  * @extends Roo.util.Observable
8557  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8558  * <br><br>
8559  * Usage:
8560  * <pre><code>
8561 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8562                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8563 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8564 split.minSize = 100;
8565 split.maxSize = 600;
8566 split.animate = true;
8567 split.on('moved', splitterMoved);
8568 </code></pre>
8569  * @constructor
8570  * Create a new SplitBar
8571  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8572  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8573  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8574  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8575                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8576                         position of the SplitBar).
8577  */
8578 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8579     
8580     /** @private */
8581     this.el = Roo.get(dragElement, true);
8582     this.el.dom.unselectable = "on";
8583     /** @private */
8584     this.resizingEl = Roo.get(resizingElement, true);
8585
8586     /**
8587      * @private
8588      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8589      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8590      * @type Number
8591      */
8592     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8593     
8594     /**
8595      * The minimum size of the resizing element. (Defaults to 0)
8596      * @type Number
8597      */
8598     this.minSize = 0;
8599     
8600     /**
8601      * The maximum size of the resizing element. (Defaults to 2000)
8602      * @type Number
8603      */
8604     this.maxSize = 2000;
8605     
8606     /**
8607      * Whether to animate the transition to the new size
8608      * @type Boolean
8609      */
8610     this.animate = false;
8611     
8612     /**
8613      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8614      * @type Boolean
8615      */
8616     this.useShim = false;
8617     
8618     /** @private */
8619     this.shim = null;
8620     
8621     if(!existingProxy){
8622         /** @private */
8623         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8624     }else{
8625         this.proxy = Roo.get(existingProxy).dom;
8626     }
8627     /** @private */
8628     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8629     
8630     /** @private */
8631     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8632     
8633     /** @private */
8634     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8635     
8636     /** @private */
8637     this.dragSpecs = {};
8638     
8639     /**
8640      * @private The adapter to use to positon and resize elements
8641      */
8642     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8643     this.adapter.init(this);
8644     
8645     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8646         /** @private */
8647         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8648         this.el.addClass("x-splitbar-h");
8649     }else{
8650         /** @private */
8651         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8652         this.el.addClass("x-splitbar-v");
8653     }
8654     
8655     this.addEvents({
8656         /**
8657          * @event resize
8658          * Fires when the splitter is moved (alias for {@link #event-moved})
8659          * @param {Roo.SplitBar} this
8660          * @param {Number} newSize the new width or height
8661          */
8662         "resize" : true,
8663         /**
8664          * @event moved
8665          * Fires when the splitter is moved
8666          * @param {Roo.SplitBar} this
8667          * @param {Number} newSize the new width or height
8668          */
8669         "moved" : true,
8670         /**
8671          * @event beforeresize
8672          * Fires before the splitter is dragged
8673          * @param {Roo.SplitBar} this
8674          */
8675         "beforeresize" : true,
8676
8677         "beforeapply" : true
8678     });
8679
8680     Roo.util.Observable.call(this);
8681 };
8682
8683 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8684     onStartProxyDrag : function(x, y){
8685         this.fireEvent("beforeresize", this);
8686         if(!this.overlay){
8687             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8688             o.unselectable();
8689             o.enableDisplayMode("block");
8690             // all splitbars share the same overlay
8691             Roo.SplitBar.prototype.overlay = o;
8692         }
8693         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8694         this.overlay.show();
8695         Roo.get(this.proxy).setDisplayed("block");
8696         var size = this.adapter.getElementSize(this);
8697         this.activeMinSize = this.getMinimumSize();;
8698         this.activeMaxSize = this.getMaximumSize();;
8699         var c1 = size - this.activeMinSize;
8700         var c2 = Math.max(this.activeMaxSize - size, 0);
8701         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8702             this.dd.resetConstraints();
8703             this.dd.setXConstraint(
8704                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8705                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8706             );
8707             this.dd.setYConstraint(0, 0);
8708         }else{
8709             this.dd.resetConstraints();
8710             this.dd.setXConstraint(0, 0);
8711             this.dd.setYConstraint(
8712                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8713                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8714             );
8715          }
8716         this.dragSpecs.startSize = size;
8717         this.dragSpecs.startPoint = [x, y];
8718         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8719     },
8720     
8721     /** 
8722      * @private Called after the drag operation by the DDProxy
8723      */
8724     onEndProxyDrag : function(e){
8725         Roo.get(this.proxy).setDisplayed(false);
8726         var endPoint = Roo.lib.Event.getXY(e);
8727         if(this.overlay){
8728             this.overlay.hide();
8729         }
8730         var newSize;
8731         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8732             newSize = this.dragSpecs.startSize + 
8733                 (this.placement == Roo.SplitBar.LEFT ?
8734                     endPoint[0] - this.dragSpecs.startPoint[0] :
8735                     this.dragSpecs.startPoint[0] - endPoint[0]
8736                 );
8737         }else{
8738             newSize = this.dragSpecs.startSize + 
8739                 (this.placement == Roo.SplitBar.TOP ?
8740                     endPoint[1] - this.dragSpecs.startPoint[1] :
8741                     this.dragSpecs.startPoint[1] - endPoint[1]
8742                 );
8743         }
8744         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8745         if(newSize != this.dragSpecs.startSize){
8746             if(this.fireEvent('beforeapply', this, newSize) !== false){
8747                 this.adapter.setElementSize(this, newSize);
8748                 this.fireEvent("moved", this, newSize);
8749                 this.fireEvent("resize", this, newSize);
8750             }
8751         }
8752     },
8753     
8754     /**
8755      * Get the adapter this SplitBar uses
8756      * @return The adapter object
8757      */
8758     getAdapter : function(){
8759         return this.adapter;
8760     },
8761     
8762     /**
8763      * Set the adapter this SplitBar uses
8764      * @param {Object} adapter A SplitBar adapter object
8765      */
8766     setAdapter : function(adapter){
8767         this.adapter = adapter;
8768         this.adapter.init(this);
8769     },
8770     
8771     /**
8772      * Gets the minimum size for the resizing element
8773      * @return {Number} The minimum size
8774      */
8775     getMinimumSize : function(){
8776         return this.minSize;
8777     },
8778     
8779     /**
8780      * Sets the minimum size for the resizing element
8781      * @param {Number} minSize The minimum size
8782      */
8783     setMinimumSize : function(minSize){
8784         this.minSize = minSize;
8785     },
8786     
8787     /**
8788      * Gets the maximum size for the resizing element
8789      * @return {Number} The maximum size
8790      */
8791     getMaximumSize : function(){
8792         return this.maxSize;
8793     },
8794     
8795     /**
8796      * Sets the maximum size for the resizing element
8797      * @param {Number} maxSize The maximum size
8798      */
8799     setMaximumSize : function(maxSize){
8800         this.maxSize = maxSize;
8801     },
8802     
8803     /**
8804      * Sets the initialize size for the resizing element
8805      * @param {Number} size The initial size
8806      */
8807     setCurrentSize : function(size){
8808         var oldAnimate = this.animate;
8809         this.animate = false;
8810         this.adapter.setElementSize(this, size);
8811         this.animate = oldAnimate;
8812     },
8813     
8814     /**
8815      * Destroy this splitbar. 
8816      * @param {Boolean} removeEl True to remove the element
8817      */
8818     destroy : function(removeEl){
8819         if(this.shim){
8820             this.shim.remove();
8821         }
8822         this.dd.unreg();
8823         this.proxy.parentNode.removeChild(this.proxy);
8824         if(removeEl){
8825             this.el.remove();
8826         }
8827     }
8828 });
8829
8830 /**
8831  * @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.
8832  */
8833 Roo.SplitBar.createProxy = function(dir){
8834     var proxy = new Roo.Element(document.createElement("div"));
8835     proxy.unselectable();
8836     var cls = 'x-splitbar-proxy';
8837     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8838     document.body.appendChild(proxy.dom);
8839     return proxy.dom;
8840 };
8841
8842 /** 
8843  * @class Roo.SplitBar.BasicLayoutAdapter
8844  * Default Adapter. It assumes the splitter and resizing element are not positioned
8845  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8846  */
8847 Roo.SplitBar.BasicLayoutAdapter = function(){
8848 };
8849
8850 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8851     // do nothing for now
8852     init : function(s){
8853     
8854     },
8855     /**
8856      * Called before drag operations to get the current size of the resizing element. 
8857      * @param {Roo.SplitBar} s The SplitBar using this adapter
8858      */
8859      getElementSize : function(s){
8860         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8861             return s.resizingEl.getWidth();
8862         }else{
8863             return s.resizingEl.getHeight();
8864         }
8865     },
8866     
8867     /**
8868      * Called after drag operations to set the size of the resizing element.
8869      * @param {Roo.SplitBar} s The SplitBar using this adapter
8870      * @param {Number} newSize The new size to set
8871      * @param {Function} onComplete A function to be invoked when resizing is complete
8872      */
8873     setElementSize : function(s, newSize, onComplete){
8874         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8875             if(!s.animate){
8876                 s.resizingEl.setWidth(newSize);
8877                 if(onComplete){
8878                     onComplete(s, newSize);
8879                 }
8880             }else{
8881                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8882             }
8883         }else{
8884             
8885             if(!s.animate){
8886                 s.resizingEl.setHeight(newSize);
8887                 if(onComplete){
8888                     onComplete(s, newSize);
8889                 }
8890             }else{
8891                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8892             }
8893         }
8894     }
8895 };
8896
8897 /** 
8898  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8899  * @extends Roo.SplitBar.BasicLayoutAdapter
8900  * Adapter that  moves the splitter element to align with the resized sizing element. 
8901  * Used with an absolute positioned SplitBar.
8902  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8903  * document.body, make sure you assign an id to the body element.
8904  */
8905 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8906     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8907     this.container = Roo.get(container);
8908 };
8909
8910 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8911     init : function(s){
8912         this.basic.init(s);
8913     },
8914     
8915     getElementSize : function(s){
8916         return this.basic.getElementSize(s);
8917     },
8918     
8919     setElementSize : function(s, newSize, onComplete){
8920         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8921     },
8922     
8923     moveSplitter : function(s){
8924         var yes = Roo.SplitBar;
8925         switch(s.placement){
8926             case yes.LEFT:
8927                 s.el.setX(s.resizingEl.getRight());
8928                 break;
8929             case yes.RIGHT:
8930                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8931                 break;
8932             case yes.TOP:
8933                 s.el.setY(s.resizingEl.getBottom());
8934                 break;
8935             case yes.BOTTOM:
8936                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
8937                 break;
8938         }
8939     }
8940 };
8941
8942 /**
8943  * Orientation constant - Create a vertical SplitBar
8944  * @static
8945  * @type Number
8946  */
8947 Roo.SplitBar.VERTICAL = 1;
8948
8949 /**
8950  * Orientation constant - Create a horizontal SplitBar
8951  * @static
8952  * @type Number
8953  */
8954 Roo.SplitBar.HORIZONTAL = 2;
8955
8956 /**
8957  * Placement constant - The resizing element is to the left of the splitter element
8958  * @static
8959  * @type Number
8960  */
8961 Roo.SplitBar.LEFT = 1;
8962
8963 /**
8964  * Placement constant - The resizing element is to the right of the splitter element
8965  * @static
8966  * @type Number
8967  */
8968 Roo.SplitBar.RIGHT = 2;
8969
8970 /**
8971  * Placement constant - The resizing element is positioned above the splitter element
8972  * @static
8973  * @type Number
8974  */
8975 Roo.SplitBar.TOP = 3;
8976
8977 /**
8978  * Placement constant - The resizing element is positioned under splitter element
8979  * @static
8980  * @type Number
8981  */
8982 Roo.SplitBar.BOTTOM = 4;
8983 /*
8984  * Based on:
8985  * Ext JS Library 1.1.1
8986  * Copyright(c) 2006-2007, Ext JS, LLC.
8987  *
8988  * Originally Released Under LGPL - original licence link has changed is not relivant.
8989  *
8990  * Fork - LGPL
8991  * <script type="text/javascript">
8992  */
8993
8994 /**
8995  * @class Roo.View
8996  * @extends Roo.util.Observable
8997  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8998  * This class also supports single and multi selection modes. <br>
8999  * Create a data model bound view:
9000  <pre><code>
9001  var store = new Roo.data.Store(...);
9002
9003  var view = new Roo.View({
9004     el : "my-element",
9005     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9006  
9007     singleSelect: true,
9008     selectedClass: "ydataview-selected",
9009     store: store
9010  });
9011
9012  // listen for node click?
9013  view.on("click", function(vw, index, node, e){
9014  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9015  });
9016
9017  // load XML data
9018  dataModel.load("foobar.xml");
9019  </code></pre>
9020  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9021  * <br><br>
9022  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9023  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9024  * 
9025  * Note: old style constructor is still suported (container, template, config)
9026  * 
9027  * @constructor
9028  * Create a new View
9029  * @param {Object} config The config object
9030  * 
9031  */
9032 Roo.View = function(config, depreciated_tpl, depreciated_config){
9033     
9034     if (typeof(depreciated_tpl) == 'undefined') {
9035         // new way.. - universal constructor.
9036         Roo.apply(this, config);
9037         this.el  = Roo.get(this.el);
9038     } else {
9039         // old format..
9040         this.el  = Roo.get(config);
9041         this.tpl = depreciated_tpl;
9042         Roo.apply(this, depreciated_config);
9043     }
9044      
9045     
9046     if(typeof(this.tpl) == "string"){
9047         this.tpl = new Roo.Template(this.tpl);
9048     } else {
9049         // support xtype ctors..
9050         this.tpl = new Roo.factory(this.tpl, Roo);
9051     }
9052     
9053     
9054     this.tpl.compile();
9055    
9056
9057      
9058     /** @private */
9059     this.addEvents({
9060     /**
9061      * @event beforeclick
9062      * Fires before a click is processed. Returns false to cancel the default action.
9063      * @param {Roo.View} this
9064      * @param {Number} index The index of the target node
9065      * @param {HTMLElement} node The target node
9066      * @param {Roo.EventObject} e The raw event object
9067      */
9068         "beforeclick" : true,
9069     /**
9070      * @event click
9071      * Fires when a template node is clicked.
9072      * @param {Roo.View} this
9073      * @param {Number} index The index of the target node
9074      * @param {HTMLElement} node The target node
9075      * @param {Roo.EventObject} e The raw event object
9076      */
9077         "click" : true,
9078     /**
9079      * @event dblclick
9080      * Fires when a template node is double clicked.
9081      * @param {Roo.View} this
9082      * @param {Number} index The index of the target node
9083      * @param {HTMLElement} node The target node
9084      * @param {Roo.EventObject} e The raw event object
9085      */
9086         "dblclick" : true,
9087     /**
9088      * @event contextmenu
9089      * Fires when a template node is right clicked.
9090      * @param {Roo.View} this
9091      * @param {Number} index The index of the target node
9092      * @param {HTMLElement} node The target node
9093      * @param {Roo.EventObject} e The raw event object
9094      */
9095         "contextmenu" : true,
9096     /**
9097      * @event selectionchange
9098      * Fires when the selected nodes change.
9099      * @param {Roo.View} this
9100      * @param {Array} selections Array of the selected nodes
9101      */
9102         "selectionchange" : true,
9103
9104     /**
9105      * @event beforeselect
9106      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9107      * @param {Roo.View} this
9108      * @param {HTMLElement} node The node to be selected
9109      * @param {Array} selections Array of currently selected nodes
9110      */
9111         "beforeselect" : true
9112     });
9113
9114     this.el.on({
9115         "click": this.onClick,
9116         "dblclick": this.onDblClick,
9117         "contextmenu": this.onContextMenu,
9118         scope:this
9119     });
9120
9121     this.selections = [];
9122     this.nodes = [];
9123     this.cmp = new Roo.CompositeElementLite([]);
9124     if(this.store){
9125         this.store = Roo.factory(this.store, Roo.data);
9126         this.setStore(this.store, true);
9127     }
9128     Roo.View.superclass.constructor.call(this);
9129 };
9130
9131 Roo.extend(Roo.View, Roo.util.Observable, {
9132     
9133      /**
9134      * @cfg {Roo.data.Store} store Data store to load data from.
9135      */
9136     store : false,
9137     
9138     /**
9139      * @cfg {String|Roo.Element} el The container element.
9140      */
9141     el : '',
9142     
9143     /**
9144      * @cfg {String|Roo.Template} tpl The template used by this View 
9145      */
9146     tpl : false,
9147     
9148     /**
9149      * @cfg {String} selectedClass The css class to add to selected nodes
9150      */
9151     selectedClass : "x-view-selected",
9152      /**
9153      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9154      */
9155     emptyText : "",
9156     /**
9157      * @cfg {Boolean} multiSelect Allow multiple selection
9158      */
9159     
9160     multiSelect : false,
9161     /**
9162      * @cfg {Boolean} singleSelect Allow single selection
9163      */
9164     singleSelect:  false,
9165     
9166     /**
9167      * Returns the element this view is bound to.
9168      * @return {Roo.Element}
9169      */
9170     getEl : function(){
9171         return this.el;
9172     },
9173
9174     /**
9175      * Refreshes the view.
9176      */
9177     refresh : function(){
9178         var t = this.tpl;
9179         this.clearSelections();
9180         this.el.update("");
9181         var html = [];
9182         var records = this.store.getRange();
9183         if(records.length < 1){
9184             this.el.update(this.emptyText);
9185             return;
9186         }
9187         for(var i = 0, len = records.length; i < len; i++){
9188             var data = this.prepareData(records[i].data, i, records[i]);
9189             html[html.length] = t.apply(data);
9190         }
9191         this.el.update(html.join(""));
9192         this.nodes = this.el.dom.childNodes;
9193         this.updateIndexes(0);
9194     },
9195
9196     /**
9197      * Function to override to reformat the data that is sent to
9198      * the template for each node.
9199      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9200      * a JSON object for an UpdateManager bound view).
9201      */
9202     prepareData : function(data){
9203         return data;
9204     },
9205
9206     onUpdate : function(ds, record){
9207         this.clearSelections();
9208         var index = this.store.indexOf(record);
9209         var n = this.nodes[index];
9210         this.tpl.insertBefore(n, this.prepareData(record.data));
9211         n.parentNode.removeChild(n);
9212         this.updateIndexes(index, index);
9213     },
9214
9215     onAdd : function(ds, records, index){
9216         this.clearSelections();
9217         if(this.nodes.length == 0){
9218             this.refresh();
9219             return;
9220         }
9221         var n = this.nodes[index];
9222         for(var i = 0, len = records.length; i < len; i++){
9223             var d = this.prepareData(records[i].data);
9224             if(n){
9225                 this.tpl.insertBefore(n, d);
9226             }else{
9227                 this.tpl.append(this.el, d);
9228             }
9229         }
9230         this.updateIndexes(index);
9231     },
9232
9233     onRemove : function(ds, record, index){
9234         this.clearSelections();
9235         this.el.dom.removeChild(this.nodes[index]);
9236         this.updateIndexes(index);
9237     },
9238
9239     /**
9240      * Refresh an individual node.
9241      * @param {Number} index
9242      */
9243     refreshNode : function(index){
9244         this.onUpdate(this.store, this.store.getAt(index));
9245     },
9246
9247     updateIndexes : function(startIndex, endIndex){
9248         var ns = this.nodes;
9249         startIndex = startIndex || 0;
9250         endIndex = endIndex || ns.length - 1;
9251         for(var i = startIndex; i <= endIndex; i++){
9252             ns[i].nodeIndex = i;
9253         }
9254     },
9255
9256     /**
9257      * Changes the data store this view uses and refresh the view.
9258      * @param {Store} store
9259      */
9260     setStore : function(store, initial){
9261         if(!initial && this.store){
9262             this.store.un("datachanged", this.refresh);
9263             this.store.un("add", this.onAdd);
9264             this.store.un("remove", this.onRemove);
9265             this.store.un("update", this.onUpdate);
9266             this.store.un("clear", this.refresh);
9267         }
9268         if(store){
9269           
9270             store.on("datachanged", this.refresh, this);
9271             store.on("add", this.onAdd, this);
9272             store.on("remove", this.onRemove, this);
9273             store.on("update", this.onUpdate, this);
9274             store.on("clear", this.refresh, this);
9275         }
9276         
9277         if(store){
9278             this.refresh();
9279         }
9280     },
9281
9282     /**
9283      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9284      * @param {HTMLElement} node
9285      * @return {HTMLElement} The template node
9286      */
9287     findItemFromChild : function(node){
9288         var el = this.el.dom;
9289         if(!node || node.parentNode == el){
9290                     return node;
9291             }
9292             var p = node.parentNode;
9293             while(p && p != el){
9294             if(p.parentNode == el){
9295                 return p;
9296             }
9297             p = p.parentNode;
9298         }
9299             return null;
9300     },
9301
9302     /** @ignore */
9303     onClick : function(e){
9304         var item = this.findItemFromChild(e.getTarget());
9305         if(item){
9306             var index = this.indexOf(item);
9307             if(this.onItemClick(item, index, e) !== false){
9308                 this.fireEvent("click", this, index, item, e);
9309             }
9310         }else{
9311             this.clearSelections();
9312         }
9313     },
9314
9315     /** @ignore */
9316     onContextMenu : function(e){
9317         var item = this.findItemFromChild(e.getTarget());
9318         if(item){
9319             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9320         }
9321     },
9322
9323     /** @ignore */
9324     onDblClick : function(e){
9325         var item = this.findItemFromChild(e.getTarget());
9326         if(item){
9327             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9328         }
9329     },
9330
9331     onItemClick : function(item, index, e){
9332         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9333             return false;
9334         }
9335         if(this.multiSelect || this.singleSelect){
9336             if(this.multiSelect && e.shiftKey && this.lastSelection){
9337                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9338             }else{
9339                 this.select(item, this.multiSelect && e.ctrlKey);
9340                 this.lastSelection = item;
9341             }
9342             e.preventDefault();
9343         }
9344         return true;
9345     },
9346
9347     /**
9348      * Get the number of selected nodes.
9349      * @return {Number}
9350      */
9351     getSelectionCount : function(){
9352         return this.selections.length;
9353     },
9354
9355     /**
9356      * Get the currently selected nodes.
9357      * @return {Array} An array of HTMLElements
9358      */
9359     getSelectedNodes : function(){
9360         return this.selections;
9361     },
9362
9363     /**
9364      * Get the indexes of the selected nodes.
9365      * @return {Array}
9366      */
9367     getSelectedIndexes : function(){
9368         var indexes = [], s = this.selections;
9369         for(var i = 0, len = s.length; i < len; i++){
9370             indexes.push(s[i].nodeIndex);
9371         }
9372         return indexes;
9373     },
9374
9375     /**
9376      * Clear all selections
9377      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9378      */
9379     clearSelections : function(suppressEvent){
9380         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9381             this.cmp.elements = this.selections;
9382             this.cmp.removeClass(this.selectedClass);
9383             this.selections = [];
9384             if(!suppressEvent){
9385                 this.fireEvent("selectionchange", this, this.selections);
9386             }
9387         }
9388     },
9389
9390     /**
9391      * Returns true if the passed node is selected
9392      * @param {HTMLElement/Number} node The node or node index
9393      * @return {Boolean}
9394      */
9395     isSelected : function(node){
9396         var s = this.selections;
9397         if(s.length < 1){
9398             return false;
9399         }
9400         node = this.getNode(node);
9401         return s.indexOf(node) !== -1;
9402     },
9403
9404     /**
9405      * Selects nodes.
9406      * @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
9407      * @param {Boolean} keepExisting (optional) true to keep existing selections
9408      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9409      */
9410     select : function(nodeInfo, keepExisting, suppressEvent){
9411         if(nodeInfo instanceof Array){
9412             if(!keepExisting){
9413                 this.clearSelections(true);
9414             }
9415             for(var i = 0, len = nodeInfo.length; i < len; i++){
9416                 this.select(nodeInfo[i], true, true);
9417             }
9418         } else{
9419             var node = this.getNode(nodeInfo);
9420             if(node && !this.isSelected(node)){
9421                 if(!keepExisting){
9422                     this.clearSelections(true);
9423                 }
9424                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9425                     Roo.fly(node).addClass(this.selectedClass);
9426                     this.selections.push(node);
9427                     if(!suppressEvent){
9428                         this.fireEvent("selectionchange", this, this.selections);
9429                     }
9430                 }
9431             }
9432         }
9433     },
9434
9435     /**
9436      * Gets a template node.
9437      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9438      * @return {HTMLElement} The node or null if it wasn't found
9439      */
9440     getNode : function(nodeInfo){
9441         if(typeof nodeInfo == "string"){
9442             return document.getElementById(nodeInfo);
9443         }else if(typeof nodeInfo == "number"){
9444             return this.nodes[nodeInfo];
9445         }
9446         return nodeInfo;
9447     },
9448
9449     /**
9450      * Gets a range template nodes.
9451      * @param {Number} startIndex
9452      * @param {Number} endIndex
9453      * @return {Array} An array of nodes
9454      */
9455     getNodes : function(start, end){
9456         var ns = this.nodes;
9457         start = start || 0;
9458         end = typeof end == "undefined" ? ns.length - 1 : end;
9459         var nodes = [];
9460         if(start <= end){
9461             for(var i = start; i <= end; i++){
9462                 nodes.push(ns[i]);
9463             }
9464         } else{
9465             for(var i = start; i >= end; i--){
9466                 nodes.push(ns[i]);
9467             }
9468         }
9469         return nodes;
9470     },
9471
9472     /**
9473      * Finds the index of the passed node
9474      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9475      * @return {Number} The index of the node or -1
9476      */
9477     indexOf : function(node){
9478         node = this.getNode(node);
9479         if(typeof node.nodeIndex == "number"){
9480             return node.nodeIndex;
9481         }
9482         var ns = this.nodes;
9483         for(var i = 0, len = ns.length; i < len; i++){
9484             if(ns[i] == node){
9485                 return i;
9486             }
9487         }
9488         return -1;
9489     }
9490 });
9491 /*
9492  * Based on:
9493  * Ext JS Library 1.1.1
9494  * Copyright(c) 2006-2007, Ext JS, LLC.
9495  *
9496  * Originally Released Under LGPL - original licence link has changed is not relivant.
9497  *
9498  * Fork - LGPL
9499  * <script type="text/javascript">
9500  */
9501
9502 /**
9503  * @class Roo.JsonView
9504  * @extends Roo.View
9505  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9506 <pre><code>
9507 var view = new Roo.JsonView({
9508     container: "my-element",
9509     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9510     multiSelect: true, 
9511     jsonRoot: "data" 
9512 });
9513
9514 // listen for node click?
9515 view.on("click", function(vw, index, node, e){
9516     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9517 });
9518
9519 // direct load of JSON data
9520 view.load("foobar.php");
9521
9522 // Example from my blog list
9523 var tpl = new Roo.Template(
9524     '&lt;div class="entry"&gt;' +
9525     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9526     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9527     "&lt;/div&gt;&lt;hr /&gt;"
9528 );
9529
9530 var moreView = new Roo.JsonView({
9531     container :  "entry-list", 
9532     template : tpl,
9533     jsonRoot: "posts"
9534 });
9535 moreView.on("beforerender", this.sortEntries, this);
9536 moreView.load({
9537     url: "/blog/get-posts.php",
9538     params: "allposts=true",
9539     text: "Loading Blog Entries..."
9540 });
9541 </code></pre>
9542
9543 * Note: old code is supported with arguments : (container, template, config)
9544
9545
9546  * @constructor
9547  * Create a new JsonView
9548  * 
9549  * @param {Object} config The config object
9550  * 
9551  */
9552 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9553     
9554     
9555     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9556
9557     var um = this.el.getUpdateManager();
9558     um.setRenderer(this);
9559     um.on("update", this.onLoad, this);
9560     um.on("failure", this.onLoadException, this);
9561
9562     /**
9563      * @event beforerender
9564      * Fires before rendering of the downloaded JSON data.
9565      * @param {Roo.JsonView} this
9566      * @param {Object} data The JSON data loaded
9567      */
9568     /**
9569      * @event load
9570      * Fires when data is loaded.
9571      * @param {Roo.JsonView} this
9572      * @param {Object} data The JSON data loaded
9573      * @param {Object} response The raw Connect response object
9574      */
9575     /**
9576      * @event loadexception
9577      * Fires when loading fails.
9578      * @param {Roo.JsonView} this
9579      * @param {Object} response The raw Connect response object
9580      */
9581     this.addEvents({
9582         'beforerender' : true,
9583         'load' : true,
9584         'loadexception' : true
9585     });
9586 };
9587 Roo.extend(Roo.JsonView, Roo.View, {
9588     /**
9589      * @type {String} The root property in the loaded JSON object that contains the data
9590      */
9591     jsonRoot : "",
9592
9593     /**
9594      * Refreshes the view.
9595      */
9596     refresh : function(){
9597         this.clearSelections();
9598         this.el.update("");
9599         var html = [];
9600         var o = this.jsonData;
9601         if(o && o.length > 0){
9602             for(var i = 0, len = o.length; i < len; i++){
9603                 var data = this.prepareData(o[i], i, o);
9604                 html[html.length] = this.tpl.apply(data);
9605             }
9606         }else{
9607             html.push(this.emptyText);
9608         }
9609         this.el.update(html.join(""));
9610         this.nodes = this.el.dom.childNodes;
9611         this.updateIndexes(0);
9612     },
9613
9614     /**
9615      * 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.
9616      * @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:
9617      <pre><code>
9618      view.load({
9619          url: "your-url.php",
9620          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9621          callback: yourFunction,
9622          scope: yourObject, //(optional scope)
9623          discardUrl: false,
9624          nocache: false,
9625          text: "Loading...",
9626          timeout: 30,
9627          scripts: false
9628      });
9629      </code></pre>
9630      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9631      * 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.
9632      * @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}
9633      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9634      * @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.
9635      */
9636     load : function(){
9637         var um = this.el.getUpdateManager();
9638         um.update.apply(um, arguments);
9639     },
9640
9641     render : function(el, response){
9642         this.clearSelections();
9643         this.el.update("");
9644         var o;
9645         try{
9646             o = Roo.util.JSON.decode(response.responseText);
9647             if(this.jsonRoot){
9648                 
9649                 o = o[this.jsonRoot];
9650             }
9651         } catch(e){
9652         }
9653         /**
9654          * The current JSON data or null
9655          */
9656         this.jsonData = o;
9657         this.beforeRender();
9658         this.refresh();
9659     },
9660
9661 /**
9662  * Get the number of records in the current JSON dataset
9663  * @return {Number}
9664  */
9665     getCount : function(){
9666         return this.jsonData ? this.jsonData.length : 0;
9667     },
9668
9669 /**
9670  * Returns the JSON object for the specified node(s)
9671  * @param {HTMLElement/Array} node The node or an array of nodes
9672  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9673  * you get the JSON object for the node
9674  */
9675     getNodeData : function(node){
9676         if(node instanceof Array){
9677             var data = [];
9678             for(var i = 0, len = node.length; i < len; i++){
9679                 data.push(this.getNodeData(node[i]));
9680             }
9681             return data;
9682         }
9683         return this.jsonData[this.indexOf(node)] || null;
9684     },
9685
9686     beforeRender : function(){
9687         this.snapshot = this.jsonData;
9688         if(this.sortInfo){
9689             this.sort.apply(this, this.sortInfo);
9690         }
9691         this.fireEvent("beforerender", this, this.jsonData);
9692     },
9693
9694     onLoad : function(el, o){
9695         this.fireEvent("load", this, this.jsonData, o);
9696     },
9697
9698     onLoadException : function(el, o){
9699         this.fireEvent("loadexception", this, o);
9700     },
9701
9702 /**
9703  * Filter the data by a specific property.
9704  * @param {String} property A property on your JSON objects
9705  * @param {String/RegExp} value Either string that the property values
9706  * should start with, or a RegExp to test against the property
9707  */
9708     filter : function(property, value){
9709         if(this.jsonData){
9710             var data = [];
9711             var ss = this.snapshot;
9712             if(typeof value == "string"){
9713                 var vlen = value.length;
9714                 if(vlen == 0){
9715                     this.clearFilter();
9716                     return;
9717                 }
9718                 value = value.toLowerCase();
9719                 for(var i = 0, len = ss.length; i < len; i++){
9720                     var o = ss[i];
9721                     if(o[property].substr(0, vlen).toLowerCase() == value){
9722                         data.push(o);
9723                     }
9724                 }
9725             } else if(value.exec){ // regex?
9726                 for(var i = 0, len = ss.length; i < len; i++){
9727                     var o = ss[i];
9728                     if(value.test(o[property])){
9729                         data.push(o);
9730                     }
9731                 }
9732             } else{
9733                 return;
9734             }
9735             this.jsonData = data;
9736             this.refresh();
9737         }
9738     },
9739
9740 /**
9741  * Filter by a function. The passed function will be called with each
9742  * object in the current dataset. If the function returns true the value is kept,
9743  * otherwise it is filtered.
9744  * @param {Function} fn
9745  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9746  */
9747     filterBy : function(fn, scope){
9748         if(this.jsonData){
9749             var data = [];
9750             var ss = this.snapshot;
9751             for(var i = 0, len = ss.length; i < len; i++){
9752                 var o = ss[i];
9753                 if(fn.call(scope || this, o)){
9754                     data.push(o);
9755                 }
9756             }
9757             this.jsonData = data;
9758             this.refresh();
9759         }
9760     },
9761
9762 /**
9763  * Clears the current filter.
9764  */
9765     clearFilter : function(){
9766         if(this.snapshot && this.jsonData != this.snapshot){
9767             this.jsonData = this.snapshot;
9768             this.refresh();
9769         }
9770     },
9771
9772
9773 /**
9774  * Sorts the data for this view and refreshes it.
9775  * @param {String} property A property on your JSON objects to sort on
9776  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9777  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9778  */
9779     sort : function(property, dir, sortType){
9780         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9781         if(this.jsonData){
9782             var p = property;
9783             var dsc = dir && dir.toLowerCase() == "desc";
9784             var f = function(o1, o2){
9785                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9786                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9787                 ;
9788                 if(v1 < v2){
9789                     return dsc ? +1 : -1;
9790                 } else if(v1 > v2){
9791                     return dsc ? -1 : +1;
9792                 } else{
9793                     return 0;
9794                 }
9795             };
9796             this.jsonData.sort(f);
9797             this.refresh();
9798             if(this.jsonData != this.snapshot){
9799                 this.snapshot.sort(f);
9800             }
9801         }
9802     }
9803 });/*
9804  * Based on:
9805  * Ext JS Library 1.1.1
9806  * Copyright(c) 2006-2007, Ext JS, LLC.
9807  *
9808  * Originally Released Under LGPL - original licence link has changed is not relivant.
9809  *
9810  * Fork - LGPL
9811  * <script type="text/javascript">
9812  */
9813  
9814
9815 /**
9816  * @class Roo.ColorPalette
9817  * @extends Roo.Component
9818  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9819  * Here's an example of typical usage:
9820  * <pre><code>
9821 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9822 cp.render('my-div');
9823
9824 cp.on('select', function(palette, selColor){
9825     // do something with selColor
9826 });
9827 </code></pre>
9828  * @constructor
9829  * Create a new ColorPalette
9830  * @param {Object} config The config object
9831  */
9832 Roo.ColorPalette = function(config){
9833     Roo.ColorPalette.superclass.constructor.call(this, config);
9834     this.addEvents({
9835         /**
9836              * @event select
9837              * Fires when a color is selected
9838              * @param {ColorPalette} this
9839              * @param {String} color The 6-digit color hex code (without the # symbol)
9840              */
9841         select: true
9842     });
9843
9844     if(this.handler){
9845         this.on("select", this.handler, this.scope, true);
9846     }
9847 };
9848 Roo.extend(Roo.ColorPalette, Roo.Component, {
9849     /**
9850      * @cfg {String} itemCls
9851      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9852      */
9853     itemCls : "x-color-palette",
9854     /**
9855      * @cfg {String} value
9856      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9857      * the hex codes are case-sensitive.
9858      */
9859     value : null,
9860     clickEvent:'click',
9861     // private
9862     ctype: "Roo.ColorPalette",
9863
9864     /**
9865      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9866      */
9867     allowReselect : false,
9868
9869     /**
9870      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9871      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9872      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9873      * of colors with the width setting until the box is symmetrical.</p>
9874      * <p>You can override individual colors if needed:</p>
9875      * <pre><code>
9876 var cp = new Roo.ColorPalette();
9877 cp.colors[0] = "FF0000";  // change the first box to red
9878 </code></pre>
9879
9880 Or you can provide a custom array of your own for complete control:
9881 <pre><code>
9882 var cp = new Roo.ColorPalette();
9883 cp.colors = ["000000", "993300", "333300"];
9884 </code></pre>
9885      * @type Array
9886      */
9887     colors : [
9888         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9889         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9890         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9891         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9892         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9893     ],
9894
9895     // private
9896     onRender : function(container, position){
9897         var t = new Roo.MasterTemplate(
9898             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9899         );
9900         var c = this.colors;
9901         for(var i = 0, len = c.length; i < len; i++){
9902             t.add([c[i]]);
9903         }
9904         var el = document.createElement("div");
9905         el.className = this.itemCls;
9906         t.overwrite(el);
9907         container.dom.insertBefore(el, position);
9908         this.el = Roo.get(el);
9909         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9910         if(this.clickEvent != 'click'){
9911             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9912         }
9913     },
9914
9915     // private
9916     afterRender : function(){
9917         Roo.ColorPalette.superclass.afterRender.call(this);
9918         if(this.value){
9919             var s = this.value;
9920             this.value = null;
9921             this.select(s);
9922         }
9923     },
9924
9925     // private
9926     handleClick : function(e, t){
9927         e.preventDefault();
9928         if(!this.disabled){
9929             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9930             this.select(c.toUpperCase());
9931         }
9932     },
9933
9934     /**
9935      * Selects the specified color in the palette (fires the select event)
9936      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9937      */
9938     select : function(color){
9939         color = color.replace("#", "");
9940         if(color != this.value || this.allowReselect){
9941             var el = this.el;
9942             if(this.value){
9943                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9944             }
9945             el.child("a.color-"+color).addClass("x-color-palette-sel");
9946             this.value = color;
9947             this.fireEvent("select", this, color);
9948         }
9949     }
9950 });/*
9951  * Based on:
9952  * Ext JS Library 1.1.1
9953  * Copyright(c) 2006-2007, Ext JS, LLC.
9954  *
9955  * Originally Released Under LGPL - original licence link has changed is not relivant.
9956  *
9957  * Fork - LGPL
9958  * <script type="text/javascript">
9959  */
9960  
9961 /**
9962  * @class Roo.DatePicker
9963  * @extends Roo.Component
9964  * Simple date picker class.
9965  * @constructor
9966  * Create a new DatePicker
9967  * @param {Object} config The config object
9968  */
9969 Roo.DatePicker = function(config){
9970     Roo.DatePicker.superclass.constructor.call(this, config);
9971
9972     this.value = config && config.value ?
9973                  config.value.clearTime() : new Date().clearTime();
9974
9975     this.addEvents({
9976         /**
9977              * @event select
9978              * Fires when a date is selected
9979              * @param {DatePicker} this
9980              * @param {Date} date The selected date
9981              */
9982         select: true
9983     });
9984
9985     if(this.handler){
9986         this.on("select", this.handler,  this.scope || this);
9987     }
9988     // build the disabledDatesRE
9989     if(!this.disabledDatesRE && this.disabledDates){
9990         var dd = this.disabledDates;
9991         var re = "(?:";
9992         for(var i = 0; i < dd.length; i++){
9993             re += dd[i];
9994             if(i != dd.length-1) re += "|";
9995         }
9996         this.disabledDatesRE = new RegExp(re + ")");
9997     }
9998 };
9999
10000 Roo.extend(Roo.DatePicker, Roo.Component, {
10001     /**
10002      * @cfg {String} todayText
10003      * The text to display on the button that selects the current date (defaults to "Today")
10004      */
10005     todayText : "Today",
10006     /**
10007      * @cfg {String} okText
10008      * The text to display on the ok button
10009      */
10010     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10011     /**
10012      * @cfg {String} cancelText
10013      * The text to display on the cancel button
10014      */
10015     cancelText : "Cancel",
10016     /**
10017      * @cfg {String} todayTip
10018      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10019      */
10020     todayTip : "{0} (Spacebar)",
10021     /**
10022      * @cfg {Date} minDate
10023      * Minimum allowable date (JavaScript date object, defaults to null)
10024      */
10025     minDate : null,
10026     /**
10027      * @cfg {Date} maxDate
10028      * Maximum allowable date (JavaScript date object, defaults to null)
10029      */
10030     maxDate : null,
10031     /**
10032      * @cfg {String} minText
10033      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10034      */
10035     minText : "This date is before the minimum date",
10036     /**
10037      * @cfg {String} maxText
10038      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10039      */
10040     maxText : "This date is after the maximum date",
10041     /**
10042      * @cfg {String} format
10043      * The default date format string which can be overriden for localization support.  The format must be
10044      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10045      */
10046     format : "m/d/y",
10047     /**
10048      * @cfg {Array} disabledDays
10049      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10050      */
10051     disabledDays : null,
10052     /**
10053      * @cfg {String} disabledDaysText
10054      * The tooltip to display when the date falls on a disabled day (defaults to "")
10055      */
10056     disabledDaysText : "",
10057     /**
10058      * @cfg {RegExp} disabledDatesRE
10059      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10060      */
10061     disabledDatesRE : null,
10062     /**
10063      * @cfg {String} disabledDatesText
10064      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10065      */
10066     disabledDatesText : "",
10067     /**
10068      * @cfg {Boolean} constrainToViewport
10069      * True to constrain the date picker to the viewport (defaults to true)
10070      */
10071     constrainToViewport : true,
10072     /**
10073      * @cfg {Array} monthNames
10074      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10075      */
10076     monthNames : Date.monthNames,
10077     /**
10078      * @cfg {Array} dayNames
10079      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10080      */
10081     dayNames : Date.dayNames,
10082     /**
10083      * @cfg {String} nextText
10084      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10085      */
10086     nextText: 'Next Month (Control+Right)',
10087     /**
10088      * @cfg {String} prevText
10089      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10090      */
10091     prevText: 'Previous Month (Control+Left)',
10092     /**
10093      * @cfg {String} monthYearText
10094      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10095      */
10096     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10097     /**
10098      * @cfg {Number} startDay
10099      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10100      */
10101     startDay : 0,
10102     /**
10103      * @cfg {Bool} showClear
10104      * Show a clear button (usefull for date form elements that can be blank.)
10105      */
10106     
10107     showClear: false,
10108     
10109     /**
10110      * Sets the value of the date field
10111      * @param {Date} value The date to set
10112      */
10113     setValue : function(value){
10114         var old = this.value;
10115         this.value = value.clearTime(true);
10116         if(this.el){
10117             this.update(this.value);
10118         }
10119     },
10120
10121     /**
10122      * Gets the current selected value of the date field
10123      * @return {Date} The selected date
10124      */
10125     getValue : function(){
10126         return this.value;
10127     },
10128
10129     // private
10130     focus : function(){
10131         if(this.el){
10132             this.update(this.activeDate);
10133         }
10134     },
10135
10136     // private
10137     onRender : function(container, position){
10138         var m = [
10139              '<table cellspacing="0">',
10140                 '<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>',
10141                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10142         var dn = this.dayNames;
10143         for(var i = 0; i < 7; i++){
10144             var d = this.startDay+i;
10145             if(d > 6){
10146                 d = d-7;
10147             }
10148             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10149         }
10150         m[m.length] = "</tr></thead><tbody><tr>";
10151         for(var i = 0; i < 42; i++) {
10152             if(i % 7 == 0 && i != 0){
10153                 m[m.length] = "</tr><tr>";
10154             }
10155             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10156         }
10157         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10158             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10159
10160         var el = document.createElement("div");
10161         el.className = "x-date-picker";
10162         el.innerHTML = m.join("");
10163
10164         container.dom.insertBefore(el, position);
10165
10166         this.el = Roo.get(el);
10167         this.eventEl = Roo.get(el.firstChild);
10168
10169         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10170             handler: this.showPrevMonth,
10171             scope: this,
10172             preventDefault:true,
10173             stopDefault:true
10174         });
10175
10176         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10177             handler: this.showNextMonth,
10178             scope: this,
10179             preventDefault:true,
10180             stopDefault:true
10181         });
10182
10183         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10184
10185         this.monthPicker = this.el.down('div.x-date-mp');
10186         this.monthPicker.enableDisplayMode('block');
10187         
10188         var kn = new Roo.KeyNav(this.eventEl, {
10189             "left" : function(e){
10190                 e.ctrlKey ?
10191                     this.showPrevMonth() :
10192                     this.update(this.activeDate.add("d", -1));
10193             },
10194
10195             "right" : function(e){
10196                 e.ctrlKey ?
10197                     this.showNextMonth() :
10198                     this.update(this.activeDate.add("d", 1));
10199             },
10200
10201             "up" : function(e){
10202                 e.ctrlKey ?
10203                     this.showNextYear() :
10204                     this.update(this.activeDate.add("d", -7));
10205             },
10206
10207             "down" : function(e){
10208                 e.ctrlKey ?
10209                     this.showPrevYear() :
10210                     this.update(this.activeDate.add("d", 7));
10211             },
10212
10213             "pageUp" : function(e){
10214                 this.showNextMonth();
10215             },
10216
10217             "pageDown" : function(e){
10218                 this.showPrevMonth();
10219             },
10220
10221             "enter" : function(e){
10222                 e.stopPropagation();
10223                 return true;
10224             },
10225
10226             scope : this
10227         });
10228
10229         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10230
10231         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10232
10233         this.el.unselectable();
10234         
10235         this.cells = this.el.select("table.x-date-inner tbody td");
10236         this.textNodes = this.el.query("table.x-date-inner tbody span");
10237
10238         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10239             text: "&#160;",
10240             tooltip: this.monthYearText
10241         });
10242
10243         this.mbtn.on('click', this.showMonthPicker, this);
10244         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10245
10246
10247         var today = (new Date()).dateFormat(this.format);
10248         
10249         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10250         if (this.showClear) {
10251             baseTb.add( new Roo.Toolbar.Fill());
10252         }
10253         baseTb.add({
10254             text: String.format(this.todayText, today),
10255             tooltip: String.format(this.todayTip, today),
10256             handler: this.selectToday,
10257             scope: this
10258         });
10259         
10260         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10261             
10262         //});
10263         if (this.showClear) {
10264             
10265             baseTb.add( new Roo.Toolbar.Fill());
10266             baseTb.add({
10267                 text: '&#160;',
10268                 cls: 'x-btn-icon x-btn-clear',
10269                 handler: function() {
10270                     //this.value = '';
10271                     this.fireEvent("select", this, '');
10272                 },
10273                 scope: this
10274             });
10275         }
10276         
10277         
10278         if(Roo.isIE){
10279             this.el.repaint();
10280         }
10281         this.update(this.value);
10282     },
10283
10284     createMonthPicker : function(){
10285         if(!this.monthPicker.dom.firstChild){
10286             var buf = ['<table border="0" cellspacing="0">'];
10287             for(var i = 0; i < 6; i++){
10288                 buf.push(
10289                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10290                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10291                     i == 0 ?
10292                     '<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>' :
10293                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10294                 );
10295             }
10296             buf.push(
10297                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10298                     this.okText,
10299                     '</button><button type="button" class="x-date-mp-cancel">',
10300                     this.cancelText,
10301                     '</button></td></tr>',
10302                 '</table>'
10303             );
10304             this.monthPicker.update(buf.join(''));
10305             this.monthPicker.on('click', this.onMonthClick, this);
10306             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10307
10308             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10309             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10310
10311             this.mpMonths.each(function(m, a, i){
10312                 i += 1;
10313                 if((i%2) == 0){
10314                     m.dom.xmonth = 5 + Math.round(i * .5);
10315                 }else{
10316                     m.dom.xmonth = Math.round((i-1) * .5);
10317                 }
10318             });
10319         }
10320     },
10321
10322     showMonthPicker : function(){
10323         this.createMonthPicker();
10324         var size = this.el.getSize();
10325         this.monthPicker.setSize(size);
10326         this.monthPicker.child('table').setSize(size);
10327
10328         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10329         this.updateMPMonth(this.mpSelMonth);
10330         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10331         this.updateMPYear(this.mpSelYear);
10332
10333         this.monthPicker.slideIn('t', {duration:.2});
10334     },
10335
10336     updateMPYear : function(y){
10337         this.mpyear = y;
10338         var ys = this.mpYears.elements;
10339         for(var i = 1; i <= 10; i++){
10340             var td = ys[i-1], y2;
10341             if((i%2) == 0){
10342                 y2 = y + Math.round(i * .5);
10343                 td.firstChild.innerHTML = y2;
10344                 td.xyear = y2;
10345             }else{
10346                 y2 = y - (5-Math.round(i * .5));
10347                 td.firstChild.innerHTML = y2;
10348                 td.xyear = y2;
10349             }
10350             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10351         }
10352     },
10353
10354     updateMPMonth : function(sm){
10355         this.mpMonths.each(function(m, a, i){
10356             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10357         });
10358     },
10359
10360     selectMPMonth: function(m){
10361         
10362     },
10363
10364     onMonthClick : function(e, t){
10365         e.stopEvent();
10366         var el = new Roo.Element(t), pn;
10367         if(el.is('button.x-date-mp-cancel')){
10368             this.hideMonthPicker();
10369         }
10370         else if(el.is('button.x-date-mp-ok')){
10371             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10372             this.hideMonthPicker();
10373         }
10374         else if(pn = el.up('td.x-date-mp-month', 2)){
10375             this.mpMonths.removeClass('x-date-mp-sel');
10376             pn.addClass('x-date-mp-sel');
10377             this.mpSelMonth = pn.dom.xmonth;
10378         }
10379         else if(pn = el.up('td.x-date-mp-year', 2)){
10380             this.mpYears.removeClass('x-date-mp-sel');
10381             pn.addClass('x-date-mp-sel');
10382             this.mpSelYear = pn.dom.xyear;
10383         }
10384         else if(el.is('a.x-date-mp-prev')){
10385             this.updateMPYear(this.mpyear-10);
10386         }
10387         else if(el.is('a.x-date-mp-next')){
10388             this.updateMPYear(this.mpyear+10);
10389         }
10390     },
10391
10392     onMonthDblClick : function(e, t){
10393         e.stopEvent();
10394         var el = new Roo.Element(t), pn;
10395         if(pn = el.up('td.x-date-mp-month', 2)){
10396             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10397             this.hideMonthPicker();
10398         }
10399         else if(pn = el.up('td.x-date-mp-year', 2)){
10400             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10401             this.hideMonthPicker();
10402         }
10403     },
10404
10405     hideMonthPicker : function(disableAnim){
10406         if(this.monthPicker){
10407             if(disableAnim === true){
10408                 this.monthPicker.hide();
10409             }else{
10410                 this.monthPicker.slideOut('t', {duration:.2});
10411             }
10412         }
10413     },
10414
10415     // private
10416     showPrevMonth : function(e){
10417         this.update(this.activeDate.add("mo", -1));
10418     },
10419
10420     // private
10421     showNextMonth : function(e){
10422         this.update(this.activeDate.add("mo", 1));
10423     },
10424
10425     // private
10426     showPrevYear : function(){
10427         this.update(this.activeDate.add("y", -1));
10428     },
10429
10430     // private
10431     showNextYear : function(){
10432         this.update(this.activeDate.add("y", 1));
10433     },
10434
10435     // private
10436     handleMouseWheel : function(e){
10437         var delta = e.getWheelDelta();
10438         if(delta > 0){
10439             this.showPrevMonth();
10440             e.stopEvent();
10441         } else if(delta < 0){
10442             this.showNextMonth();
10443             e.stopEvent();
10444         }
10445     },
10446
10447     // private
10448     handleDateClick : function(e, t){
10449         e.stopEvent();
10450         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10451             this.setValue(new Date(t.dateValue));
10452             this.fireEvent("select", this, this.value);
10453         }
10454     },
10455
10456     // private
10457     selectToday : function(){
10458         this.setValue(new Date().clearTime());
10459         this.fireEvent("select", this, this.value);
10460     },
10461
10462     // private
10463     update : function(date){
10464         var vd = this.activeDate;
10465         this.activeDate = date;
10466         if(vd && this.el){
10467             var t = date.getTime();
10468             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10469                 this.cells.removeClass("x-date-selected");
10470                 this.cells.each(function(c){
10471                    if(c.dom.firstChild.dateValue == t){
10472                        c.addClass("x-date-selected");
10473                        setTimeout(function(){
10474                             try{c.dom.firstChild.focus();}catch(e){}
10475                        }, 50);
10476                        return false;
10477                    }
10478                 });
10479                 return;
10480             }
10481         }
10482         var days = date.getDaysInMonth();
10483         var firstOfMonth = date.getFirstDateOfMonth();
10484         var startingPos = firstOfMonth.getDay()-this.startDay;
10485
10486         if(startingPos <= this.startDay){
10487             startingPos += 7;
10488         }
10489
10490         var pm = date.add("mo", -1);
10491         var prevStart = pm.getDaysInMonth()-startingPos;
10492
10493         var cells = this.cells.elements;
10494         var textEls = this.textNodes;
10495         days += startingPos;
10496
10497         // convert everything to numbers so it's fast
10498         var day = 86400000;
10499         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10500         var today = new Date().clearTime().getTime();
10501         var sel = date.clearTime().getTime();
10502         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10503         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10504         var ddMatch = this.disabledDatesRE;
10505         var ddText = this.disabledDatesText;
10506         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10507         var ddaysText = this.disabledDaysText;
10508         var format = this.format;
10509
10510         var setCellClass = function(cal, cell){
10511             cell.title = "";
10512             var t = d.getTime();
10513             cell.firstChild.dateValue = t;
10514             if(t == today){
10515                 cell.className += " x-date-today";
10516                 cell.title = cal.todayText;
10517             }
10518             if(t == sel){
10519                 cell.className += " x-date-selected";
10520                 setTimeout(function(){
10521                     try{cell.firstChild.focus();}catch(e){}
10522                 }, 50);
10523             }
10524             // disabling
10525             if(t < min) {
10526                 cell.className = " x-date-disabled";
10527                 cell.title = cal.minText;
10528                 return;
10529             }
10530             if(t > max) {
10531                 cell.className = " x-date-disabled";
10532                 cell.title = cal.maxText;
10533                 return;
10534             }
10535             if(ddays){
10536                 if(ddays.indexOf(d.getDay()) != -1){
10537                     cell.title = ddaysText;
10538                     cell.className = " x-date-disabled";
10539                 }
10540             }
10541             if(ddMatch && format){
10542                 var fvalue = d.dateFormat(format);
10543                 if(ddMatch.test(fvalue)){
10544                     cell.title = ddText.replace("%0", fvalue);
10545                     cell.className = " x-date-disabled";
10546                 }
10547             }
10548         };
10549
10550         var i = 0;
10551         for(; i < startingPos; i++) {
10552             textEls[i].innerHTML = (++prevStart);
10553             d.setDate(d.getDate()+1);
10554             cells[i].className = "x-date-prevday";
10555             setCellClass(this, cells[i]);
10556         }
10557         for(; i < days; i++){
10558             intDay = i - startingPos + 1;
10559             textEls[i].innerHTML = (intDay);
10560             d.setDate(d.getDate()+1);
10561             cells[i].className = "x-date-active";
10562             setCellClass(this, cells[i]);
10563         }
10564         var extraDays = 0;
10565         for(; i < 42; i++) {
10566              textEls[i].innerHTML = (++extraDays);
10567              d.setDate(d.getDate()+1);
10568              cells[i].className = "x-date-nextday";
10569              setCellClass(this, cells[i]);
10570         }
10571
10572         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10573
10574         if(!this.internalRender){
10575             var main = this.el.dom.firstChild;
10576             var w = main.offsetWidth;
10577             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10578             Roo.fly(main).setWidth(w);
10579             this.internalRender = true;
10580             // opera does not respect the auto grow header center column
10581             // then, after it gets a width opera refuses to recalculate
10582             // without a second pass
10583             if(Roo.isOpera && !this.secondPass){
10584                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10585                 this.secondPass = true;
10586                 this.update.defer(10, this, [date]);
10587             }
10588         }
10589     }
10590 });/*
10591  * Based on:
10592  * Ext JS Library 1.1.1
10593  * Copyright(c) 2006-2007, Ext JS, LLC.
10594  *
10595  * Originally Released Under LGPL - original licence link has changed is not relivant.
10596  *
10597  * Fork - LGPL
10598  * <script type="text/javascript">
10599  */
10600 /**
10601  * @class Roo.TabPanel
10602  * @extends Roo.util.Observable
10603  * A lightweight tab container.
10604  * <br><br>
10605  * Usage:
10606  * <pre><code>
10607 // basic tabs 1, built from existing content
10608 var tabs = new Roo.TabPanel("tabs1");
10609 tabs.addTab("script", "View Script");
10610 tabs.addTab("markup", "View Markup");
10611 tabs.activate("script");
10612
10613 // more advanced tabs, built from javascript
10614 var jtabs = new Roo.TabPanel("jtabs");
10615 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10616
10617 // set up the UpdateManager
10618 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10619 var updater = tab2.getUpdateManager();
10620 updater.setDefaultUrl("ajax1.htm");
10621 tab2.on('activate', updater.refresh, updater, true);
10622
10623 // Use setUrl for Ajax loading
10624 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10625 tab3.setUrl("ajax2.htm", null, true);
10626
10627 // Disabled tab
10628 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10629 tab4.disable();
10630
10631 jtabs.activate("jtabs-1");
10632  * </code></pre>
10633  * @constructor
10634  * Create a new TabPanel.
10635  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10636  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10637  */
10638 Roo.TabPanel = function(container, config){
10639     /**
10640     * The container element for this TabPanel.
10641     * @type Roo.Element
10642     */
10643     this.el = Roo.get(container, true);
10644     if(config){
10645         if(typeof config == "boolean"){
10646             this.tabPosition = config ? "bottom" : "top";
10647         }else{
10648             Roo.apply(this, config);
10649         }
10650     }
10651     if(this.tabPosition == "bottom"){
10652         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10653         this.el.addClass("x-tabs-bottom");
10654     }
10655     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10656     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10657     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10658     if(Roo.isIE){
10659         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10660     }
10661     if(this.tabPosition != "bottom"){
10662     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10663      * @type Roo.Element
10664      */
10665       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10666       this.el.addClass("x-tabs-top");
10667     }
10668     this.items = [];
10669
10670     this.bodyEl.setStyle("position", "relative");
10671
10672     this.active = null;
10673     this.activateDelegate = this.activate.createDelegate(this);
10674
10675     this.addEvents({
10676         /**
10677          * @event tabchange
10678          * Fires when the active tab changes
10679          * @param {Roo.TabPanel} this
10680          * @param {Roo.TabPanelItem} activePanel The new active tab
10681          */
10682         "tabchange": true,
10683         /**
10684          * @event beforetabchange
10685          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10686          * @param {Roo.TabPanel} this
10687          * @param {Object} e Set cancel to true on this object to cancel the tab change
10688          * @param {Roo.TabPanelItem} tab The tab being changed to
10689          */
10690         "beforetabchange" : true
10691     });
10692
10693     Roo.EventManager.onWindowResize(this.onResize, this);
10694     this.cpad = this.el.getPadding("lr");
10695     this.hiddenCount = 0;
10696
10697     Roo.TabPanel.superclass.constructor.call(this);
10698 };
10699
10700 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10701         /*
10702          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10703          */
10704     tabPosition : "top",
10705         /*
10706          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10707          */
10708     currentTabWidth : 0,
10709         /*
10710          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10711          */
10712     minTabWidth : 40,
10713         /*
10714          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10715          */
10716     maxTabWidth : 250,
10717         /*
10718          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10719          */
10720     preferredTabWidth : 175,
10721         /*
10722          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10723          */
10724     resizeTabs : false,
10725         /*
10726          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10727          */
10728     monitorResize : true,
10729
10730     /**
10731      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10732      * @param {String} id The id of the div to use <b>or create</b>
10733      * @param {String} text The text for the tab
10734      * @param {String} content (optional) Content to put in the TabPanelItem body
10735      * @param {Boolean} closable (optional) True to create a close icon on the tab
10736      * @return {Roo.TabPanelItem} The created TabPanelItem
10737      */
10738     addTab : function(id, text, content, closable){
10739         var item = new Roo.TabPanelItem(this, id, text, closable);
10740         this.addTabItem(item);
10741         if(content){
10742             item.setContent(content);
10743         }
10744         return item;
10745     },
10746
10747     /**
10748      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10749      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10750      * @return {Roo.TabPanelItem}
10751      */
10752     getTab : function(id){
10753         return this.items[id];
10754     },
10755
10756     /**
10757      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10758      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10759      */
10760     hideTab : function(id){
10761         var t = this.items[id];
10762         if(!t.isHidden()){
10763            t.setHidden(true);
10764            this.hiddenCount++;
10765            this.autoSizeTabs();
10766         }
10767     },
10768
10769     /**
10770      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10771      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10772      */
10773     unhideTab : function(id){
10774         var t = this.items[id];
10775         if(t.isHidden()){
10776            t.setHidden(false);
10777            this.hiddenCount--;
10778            this.autoSizeTabs();
10779         }
10780     },
10781
10782     /**
10783      * Adds an existing {@link Roo.TabPanelItem}.
10784      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10785      */
10786     addTabItem : function(item){
10787         this.items[item.id] = item;
10788         this.items.push(item);
10789         if(this.resizeTabs){
10790            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10791            this.autoSizeTabs();
10792         }else{
10793             item.autoSize();
10794         }
10795     },
10796
10797     /**
10798      * Removes a {@link Roo.TabPanelItem}.
10799      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10800      */
10801     removeTab : function(id){
10802         var items = this.items;
10803         var tab = items[id];
10804         if(!tab) { return; }
10805         var index = items.indexOf(tab);
10806         if(this.active == tab && items.length > 1){
10807             var newTab = this.getNextAvailable(index);
10808             if(newTab) {
10809                 newTab.activate();
10810             }
10811         }
10812         this.stripEl.dom.removeChild(tab.pnode.dom);
10813         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10814             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10815         }
10816         items.splice(index, 1);
10817         delete this.items[tab.id];
10818         tab.fireEvent("close", tab);
10819         tab.purgeListeners();
10820         this.autoSizeTabs();
10821     },
10822
10823     getNextAvailable : function(start){
10824         var items = this.items;
10825         var index = start;
10826         // look for a next tab that will slide over to
10827         // replace the one being removed
10828         while(index < items.length){
10829             var item = items[++index];
10830             if(item && !item.isHidden()){
10831                 return item;
10832             }
10833         }
10834         // if one isn't found select the previous tab (on the left)
10835         index = start;
10836         while(index >= 0){
10837             var item = items[--index];
10838             if(item && !item.isHidden()){
10839                 return item;
10840             }
10841         }
10842         return null;
10843     },
10844
10845     /**
10846      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10847      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10848      */
10849     disableTab : function(id){
10850         var tab = this.items[id];
10851         if(tab && this.active != tab){
10852             tab.disable();
10853         }
10854     },
10855
10856     /**
10857      * Enables a {@link Roo.TabPanelItem} that is disabled.
10858      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10859      */
10860     enableTab : function(id){
10861         var tab = this.items[id];
10862         tab.enable();
10863     },
10864
10865     /**
10866      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10867      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10868      * @return {Roo.TabPanelItem} The TabPanelItem.
10869      */
10870     activate : function(id){
10871         var tab = this.items[id];
10872         if(!tab){
10873             return null;
10874         }
10875         if(tab == this.active || tab.disabled){
10876             return tab;
10877         }
10878         var e = {};
10879         this.fireEvent("beforetabchange", this, e, tab);
10880         if(e.cancel !== true && !tab.disabled){
10881             if(this.active){
10882                 this.active.hide();
10883             }
10884             this.active = this.items[id];
10885             this.active.show();
10886             this.fireEvent("tabchange", this, this.active);
10887         }
10888         return tab;
10889     },
10890
10891     /**
10892      * Gets the active {@link Roo.TabPanelItem}.
10893      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10894      */
10895     getActiveTab : function(){
10896         return this.active;
10897     },
10898
10899     /**
10900      * Updates the tab body element to fit the height of the container element
10901      * for overflow scrolling
10902      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10903      */
10904     syncHeight : function(targetHeight){
10905         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10906         var bm = this.bodyEl.getMargins();
10907         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10908         this.bodyEl.setHeight(newHeight);
10909         return newHeight;
10910     },
10911
10912     onResize : function(){
10913         if(this.monitorResize){
10914             this.autoSizeTabs();
10915         }
10916     },
10917
10918     /**
10919      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10920      */
10921     beginUpdate : function(){
10922         this.updating = true;
10923     },
10924
10925     /**
10926      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10927      */
10928     endUpdate : function(){
10929         this.updating = false;
10930         this.autoSizeTabs();
10931     },
10932
10933     /**
10934      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10935      */
10936     autoSizeTabs : function(){
10937         var count = this.items.length;
10938         var vcount = count - this.hiddenCount;
10939         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
10940         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10941         var availWidth = Math.floor(w / vcount);
10942         var b = this.stripBody;
10943         if(b.getWidth() > w){
10944             var tabs = this.items;
10945             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10946             if(availWidth < this.minTabWidth){
10947                 /*if(!this.sleft){    // incomplete scrolling code
10948                     this.createScrollButtons();
10949                 }
10950                 this.showScroll();
10951                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10952             }
10953         }else{
10954             if(this.currentTabWidth < this.preferredTabWidth){
10955                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10956             }
10957         }
10958     },
10959
10960     /**
10961      * Returns the number of tabs in this TabPanel.
10962      * @return {Number}
10963      */
10964      getCount : function(){
10965          return this.items.length;
10966      },
10967
10968     /**
10969      * Resizes all the tabs to the passed width
10970      * @param {Number} The new width
10971      */
10972     setTabWidth : function(width){
10973         this.currentTabWidth = width;
10974         for(var i = 0, len = this.items.length; i < len; i++) {
10975                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
10976         }
10977     },
10978
10979     /**
10980      * Destroys this TabPanel
10981      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10982      */
10983     destroy : function(removeEl){
10984         Roo.EventManager.removeResizeListener(this.onResize, this);
10985         for(var i = 0, len = this.items.length; i < len; i++){
10986             this.items[i].purgeListeners();
10987         }
10988         if(removeEl === true){
10989             this.el.update("");
10990             this.el.remove();
10991         }
10992     }
10993 });
10994
10995 /**
10996  * @class Roo.TabPanelItem
10997  * @extends Roo.util.Observable
10998  * Represents an individual item (tab plus body) in a TabPanel.
10999  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11000  * @param {String} id The id of this TabPanelItem
11001  * @param {String} text The text for the tab of this TabPanelItem
11002  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11003  */
11004 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11005     /**
11006      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11007      * @type Roo.TabPanel
11008      */
11009     this.tabPanel = tabPanel;
11010     /**
11011      * The id for this TabPanelItem
11012      * @type String
11013      */
11014     this.id = id;
11015     /** @private */
11016     this.disabled = false;
11017     /** @private */
11018     this.text = text;
11019     /** @private */
11020     this.loaded = false;
11021     this.closable = closable;
11022
11023     /**
11024      * The body element for this TabPanelItem.
11025      * @type Roo.Element
11026      */
11027     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11028     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11029     this.bodyEl.setStyle("display", "block");
11030     this.bodyEl.setStyle("zoom", "1");
11031     this.hideAction();
11032
11033     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11034     /** @private */
11035     this.el = Roo.get(els.el, true);
11036     this.inner = Roo.get(els.inner, true);
11037     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11038     this.pnode = Roo.get(els.el.parentNode, true);
11039     this.el.on("mousedown", this.onTabMouseDown, this);
11040     this.el.on("click", this.onTabClick, this);
11041     /** @private */
11042     if(closable){
11043         var c = Roo.get(els.close, true);
11044         c.dom.title = this.closeText;
11045         c.addClassOnOver("close-over");
11046         c.on("click", this.closeClick, this);
11047      }
11048
11049     this.addEvents({
11050          /**
11051          * @event activate
11052          * Fires when this tab becomes the active tab.
11053          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11054          * @param {Roo.TabPanelItem} this
11055          */
11056         "activate": true,
11057         /**
11058          * @event beforeclose
11059          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11060          * @param {Roo.TabPanelItem} this
11061          * @param {Object} e Set cancel to true on this object to cancel the close.
11062          */
11063         "beforeclose": true,
11064         /**
11065          * @event close
11066          * Fires when this tab is closed.
11067          * @param {Roo.TabPanelItem} this
11068          */
11069          "close": true,
11070         /**
11071          * @event deactivate
11072          * Fires when this tab is no longer the active tab.
11073          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11074          * @param {Roo.TabPanelItem} this
11075          */
11076          "deactivate" : true
11077     });
11078     this.hidden = false;
11079
11080     Roo.TabPanelItem.superclass.constructor.call(this);
11081 };
11082
11083 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11084     purgeListeners : function(){
11085        Roo.util.Observable.prototype.purgeListeners.call(this);
11086        this.el.removeAllListeners();
11087     },
11088     /**
11089      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11090      */
11091     show : function(){
11092         this.pnode.addClass("on");
11093         this.showAction();
11094         if(Roo.isOpera){
11095             this.tabPanel.stripWrap.repaint();
11096         }
11097         this.fireEvent("activate", this.tabPanel, this);
11098     },
11099
11100     /**
11101      * Returns true if this tab is the active tab.
11102      * @return {Boolean}
11103      */
11104     isActive : function(){
11105         return this.tabPanel.getActiveTab() == this;
11106     },
11107
11108     /**
11109      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11110      */
11111     hide : function(){
11112         this.pnode.removeClass("on");
11113         this.hideAction();
11114         this.fireEvent("deactivate", this.tabPanel, this);
11115     },
11116
11117     hideAction : function(){
11118         this.bodyEl.hide();
11119         this.bodyEl.setStyle("position", "absolute");
11120         this.bodyEl.setLeft("-20000px");
11121         this.bodyEl.setTop("-20000px");
11122     },
11123
11124     showAction : function(){
11125         this.bodyEl.setStyle("position", "relative");
11126         this.bodyEl.setTop("");
11127         this.bodyEl.setLeft("");
11128         this.bodyEl.show();
11129     },
11130
11131     /**
11132      * Set the tooltip for the tab.
11133      * @param {String} tooltip The tab's tooltip
11134      */
11135     setTooltip : function(text){
11136         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11137             this.textEl.dom.qtip = text;
11138             this.textEl.dom.removeAttribute('title');
11139         }else{
11140             this.textEl.dom.title = text;
11141         }
11142     },
11143
11144     onTabClick : function(e){
11145         e.preventDefault();
11146         this.tabPanel.activate(this.id);
11147     },
11148
11149     onTabMouseDown : function(e){
11150         e.preventDefault();
11151         this.tabPanel.activate(this.id);
11152     },
11153
11154     getWidth : function(){
11155         return this.inner.getWidth();
11156     },
11157
11158     setWidth : function(width){
11159         var iwidth = width - this.pnode.getPadding("lr");
11160         this.inner.setWidth(iwidth);
11161         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11162         this.pnode.setWidth(width);
11163     },
11164
11165     /**
11166      * Show or hide the tab
11167      * @param {Boolean} hidden True to hide or false to show.
11168      */
11169     setHidden : function(hidden){
11170         this.hidden = hidden;
11171         this.pnode.setStyle("display", hidden ? "none" : "");
11172     },
11173
11174     /**
11175      * Returns true if this tab is "hidden"
11176      * @return {Boolean}
11177      */
11178     isHidden : function(){
11179         return this.hidden;
11180     },
11181
11182     /**
11183      * Returns the text for this tab
11184      * @return {String}
11185      */
11186     getText : function(){
11187         return this.text;
11188     },
11189
11190     autoSize : function(){
11191         //this.el.beginMeasure();
11192         this.textEl.setWidth(1);
11193         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11194         //this.el.endMeasure();
11195     },
11196
11197     /**
11198      * Sets the text for the tab (Note: this also sets the tooltip text)
11199      * @param {String} text The tab's text and tooltip
11200      */
11201     setText : function(text){
11202         this.text = text;
11203         this.textEl.update(text);
11204         this.setTooltip(text);
11205         if(!this.tabPanel.resizeTabs){
11206             this.autoSize();
11207         }
11208     },
11209     /**
11210      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11211      */
11212     activate : function(){
11213         this.tabPanel.activate(this.id);
11214     },
11215
11216     /**
11217      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11218      */
11219     disable : function(){
11220         if(this.tabPanel.active != this){
11221             this.disabled = true;
11222             this.pnode.addClass("disabled");
11223         }
11224     },
11225
11226     /**
11227      * Enables this TabPanelItem if it was previously disabled.
11228      */
11229     enable : function(){
11230         this.disabled = false;
11231         this.pnode.removeClass("disabled");
11232     },
11233
11234     /**
11235      * Sets the content for this TabPanelItem.
11236      * @param {String} content The content
11237      * @param {Boolean} loadScripts true to look for and load scripts
11238      */
11239     setContent : function(content, loadScripts){
11240         this.bodyEl.update(content, loadScripts);
11241     },
11242
11243     /**
11244      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11245      * @return {Roo.UpdateManager} The UpdateManager
11246      */
11247     getUpdateManager : function(){
11248         return this.bodyEl.getUpdateManager();
11249     },
11250
11251     /**
11252      * Set a URL to be used to load the content for this TabPanelItem.
11253      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11254      * @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)
11255      * @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)
11256      * @return {Roo.UpdateManager} The UpdateManager
11257      */
11258     setUrl : function(url, params, loadOnce){
11259         if(this.refreshDelegate){
11260             this.un('activate', this.refreshDelegate);
11261         }
11262         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11263         this.on("activate", this.refreshDelegate);
11264         return this.bodyEl.getUpdateManager();
11265     },
11266
11267     /** @private */
11268     _handleRefresh : function(url, params, loadOnce){
11269         if(!loadOnce || !this.loaded){
11270             var updater = this.bodyEl.getUpdateManager();
11271             updater.update(url, params, this._setLoaded.createDelegate(this));
11272         }
11273     },
11274
11275     /**
11276      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11277      *   Will fail silently if the setUrl method has not been called.
11278      *   This does not activate the panel, just updates its content.
11279      */
11280     refresh : function(){
11281         if(this.refreshDelegate){
11282            this.loaded = false;
11283            this.refreshDelegate();
11284         }
11285     },
11286
11287     /** @private */
11288     _setLoaded : function(){
11289         this.loaded = true;
11290     },
11291
11292     /** @private */
11293     closeClick : function(e){
11294         var o = {};
11295         e.stopEvent();
11296         this.fireEvent("beforeclose", this, o);
11297         if(o.cancel !== true){
11298             this.tabPanel.removeTab(this.id);
11299         }
11300     },
11301     /**
11302      * The text displayed in the tooltip for the close icon.
11303      * @type String
11304      */
11305     closeText : "Close this tab"
11306 });
11307
11308 /** @private */
11309 Roo.TabPanel.prototype.createStrip = function(container){
11310     var strip = document.createElement("div");
11311     strip.className = "x-tabs-wrap";
11312     container.appendChild(strip);
11313     return strip;
11314 };
11315 /** @private */
11316 Roo.TabPanel.prototype.createStripList = function(strip){
11317     // div wrapper for retard IE
11318     strip.innerHTML = '<div class="x-tabs-strip-wrap"><table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr></tr></tbody></table></div>';
11319     return strip.firstChild.firstChild.firstChild.firstChild;
11320 };
11321 /** @private */
11322 Roo.TabPanel.prototype.createBody = function(container){
11323     var body = document.createElement("div");
11324     Roo.id(body, "tab-body");
11325     Roo.fly(body).addClass("x-tabs-body");
11326     container.appendChild(body);
11327     return body;
11328 };
11329 /** @private */
11330 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11331     var body = Roo.getDom(id);
11332     if(!body){
11333         body = document.createElement("div");
11334         body.id = id;
11335     }
11336     Roo.fly(body).addClass("x-tabs-item-body");
11337     bodyEl.insertBefore(body, bodyEl.firstChild);
11338     return body;
11339 };
11340 /** @private */
11341 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11342     var td = document.createElement("td");
11343     stripEl.appendChild(td);
11344     if(closable){
11345         td.className = "x-tabs-closable";
11346         if(!this.closeTpl){
11347             this.closeTpl = new Roo.Template(
11348                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11349                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11350                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11351             );
11352         }
11353         var el = this.closeTpl.overwrite(td, {"text": text});
11354         var close = el.getElementsByTagName("div")[0];
11355         var inner = el.getElementsByTagName("em")[0];
11356         return {"el": el, "close": close, "inner": inner};
11357     } else {
11358         if(!this.tabTpl){
11359             this.tabTpl = new Roo.Template(
11360                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11361                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11362             );
11363         }
11364         var el = this.tabTpl.overwrite(td, {"text": text});
11365         var inner = el.getElementsByTagName("em")[0];
11366         return {"el": el, "inner": inner};
11367     }
11368 };/*
11369  * Based on:
11370  * Ext JS Library 1.1.1
11371  * Copyright(c) 2006-2007, Ext JS, LLC.
11372  *
11373  * Originally Released Under LGPL - original licence link has changed is not relivant.
11374  *
11375  * Fork - LGPL
11376  * <script type="text/javascript">
11377  */
11378
11379 /**
11380  * @class Roo.Button
11381  * @extends Roo.util.Observable
11382  * Simple Button class
11383  * @cfg {String} text The button text
11384  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11385  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11386  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11387  * @cfg {Object} scope The scope of the handler
11388  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11389  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11390  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11391  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11392  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11393  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11394    applies if enableToggle = true)
11395  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11396  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11397   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11398  * @constructor
11399  * Create a new button
11400  * @param {Object} config The config object
11401  */
11402 Roo.Button = function(renderTo, config)
11403 {
11404     if (!config) {
11405         config = renderTo;
11406         renderTo = config.renderTo || false;
11407     }
11408     
11409     Roo.apply(this, config);
11410     this.addEvents({
11411         /**
11412              * @event click
11413              * Fires when this button is clicked
11414              * @param {Button} this
11415              * @param {EventObject} e The click event
11416              */
11417             "click" : true,
11418         /**
11419              * @event toggle
11420              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11421              * @param {Button} this
11422              * @param {Boolean} pressed
11423              */
11424             "toggle" : true,
11425         /**
11426              * @event mouseover
11427              * Fires when the mouse hovers over the button
11428              * @param {Button} this
11429              * @param {Event} e The event object
11430              */
11431         'mouseover' : true,
11432         /**
11433              * @event mouseout
11434              * Fires when the mouse exits the button
11435              * @param {Button} this
11436              * @param {Event} e The event object
11437              */
11438         'mouseout': true,
11439          /**
11440              * @event render
11441              * Fires when the button is rendered
11442              * @param {Button} this
11443              */
11444         'render': true
11445     });
11446     if(this.menu){
11447         this.menu = Roo.menu.MenuMgr.get(this.menu);
11448     }
11449     if(renderTo){
11450         this.render(renderTo);
11451     }
11452     
11453     Roo.util.Observable.call(this);
11454 };
11455
11456 Roo.extend(Roo.Button, Roo.util.Observable, {
11457     /**
11458      * 
11459      */
11460     
11461     /**
11462      * Read-only. True if this button is hidden
11463      * @type Boolean
11464      */
11465     hidden : false,
11466     /**
11467      * Read-only. True if this button is disabled
11468      * @type Boolean
11469      */
11470     disabled : false,
11471     /**
11472      * Read-only. True if this button is pressed (only if enableToggle = true)
11473      * @type Boolean
11474      */
11475     pressed : false,
11476
11477     /**
11478      * @cfg {Number} tabIndex 
11479      * The DOM tabIndex for this button (defaults to undefined)
11480      */
11481     tabIndex : undefined,
11482
11483     /**
11484      * @cfg {Boolean} enableToggle
11485      * True to enable pressed/not pressed toggling (defaults to false)
11486      */
11487     enableToggle: false,
11488     /**
11489      * @cfg {Mixed} menu
11490      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11491      */
11492     menu : undefined,
11493     /**
11494      * @cfg {String} menuAlign
11495      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11496      */
11497     menuAlign : "tl-bl?",
11498
11499     /**
11500      * @cfg {String} iconCls
11501      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11502      */
11503     iconCls : undefined,
11504     /**
11505      * @cfg {String} type
11506      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11507      */
11508     type : 'button',
11509
11510     // private
11511     menuClassTarget: 'tr',
11512
11513     /**
11514      * @cfg {String} clickEvent
11515      * The type of event to map to the button's event handler (defaults to 'click')
11516      */
11517     clickEvent : 'click',
11518
11519     /**
11520      * @cfg {Boolean} handleMouseEvents
11521      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11522      */
11523     handleMouseEvents : true,
11524
11525     /**
11526      * @cfg {String} tooltipType
11527      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11528      */
11529     tooltipType : 'qtip',
11530
11531     /**
11532      * @cfg {String} cls
11533      * A CSS class to apply to the button's main element.
11534      */
11535     
11536     /**
11537      * @cfg {Roo.Template} template (Optional)
11538      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11539      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11540      * require code modifications if required elements (e.g. a button) aren't present.
11541      */
11542
11543     // private
11544     render : function(renderTo){
11545         var btn;
11546         if(this.hideParent){
11547             this.parentEl = Roo.get(renderTo);
11548         }
11549         if(!this.dhconfig){
11550             if(!this.template){
11551                 if(!Roo.Button.buttonTemplate){
11552                     // hideous table template
11553                     Roo.Button.buttonTemplate = new Roo.Template(
11554                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11555                         '<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>',
11556                         "</tr></tbody></table>");
11557                 }
11558                 this.template = Roo.Button.buttonTemplate;
11559             }
11560             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11561             var btnEl = btn.child("button:first");
11562             btnEl.on('focus', this.onFocus, this);
11563             btnEl.on('blur', this.onBlur, this);
11564             if(this.cls){
11565                 btn.addClass(this.cls);
11566             }
11567             if(this.icon){
11568                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11569             }
11570             if(this.iconCls){
11571                 btnEl.addClass(this.iconCls);
11572                 if(!this.cls){
11573                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11574                 }
11575             }
11576             if(this.tabIndex !== undefined){
11577                 btnEl.dom.tabIndex = this.tabIndex;
11578             }
11579             if(this.tooltip){
11580                 if(typeof this.tooltip == 'object'){
11581                     Roo.QuickTips.tips(Roo.apply({
11582                           target: btnEl.id
11583                     }, this.tooltip));
11584                 } else {
11585                     btnEl.dom[this.tooltipType] = this.tooltip;
11586                 }
11587             }
11588         }else{
11589             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11590         }
11591         this.el = btn;
11592         if(this.id){
11593             this.el.dom.id = this.el.id = this.id;
11594         }
11595         if(this.menu){
11596             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11597             this.menu.on("show", this.onMenuShow, this);
11598             this.menu.on("hide", this.onMenuHide, this);
11599         }
11600         btn.addClass("x-btn");
11601         if(Roo.isIE && !Roo.isIE7){
11602             this.autoWidth.defer(1, this);
11603         }else{
11604             this.autoWidth();
11605         }
11606         if(this.handleMouseEvents){
11607             btn.on("mouseover", this.onMouseOver, this);
11608             btn.on("mouseout", this.onMouseOut, this);
11609             btn.on("mousedown", this.onMouseDown, this);
11610         }
11611         btn.on(this.clickEvent, this.onClick, this);
11612         //btn.on("mouseup", this.onMouseUp, this);
11613         if(this.hidden){
11614             this.hide();
11615         }
11616         if(this.disabled){
11617             this.disable();
11618         }
11619         Roo.ButtonToggleMgr.register(this);
11620         if(this.pressed){
11621             this.el.addClass("x-btn-pressed");
11622         }
11623         if(this.repeat){
11624             var repeater = new Roo.util.ClickRepeater(btn,
11625                 typeof this.repeat == "object" ? this.repeat : {}
11626             );
11627             repeater.on("click", this.onClick,  this);
11628         }
11629         this.fireEvent('render', this);
11630         
11631     },
11632     /**
11633      * Returns the button's underlying element
11634      * @return {Roo.Element} The element
11635      */
11636     getEl : function(){
11637         return this.el;  
11638     },
11639     
11640     /**
11641      * Destroys this Button and removes any listeners.
11642      */
11643     destroy : function(){
11644         Roo.ButtonToggleMgr.unregister(this);
11645         this.el.removeAllListeners();
11646         this.purgeListeners();
11647         this.el.remove();
11648     },
11649
11650     // private
11651     autoWidth : function(){
11652         if(this.el){
11653             this.el.setWidth("auto");
11654             if(Roo.isIE7 && Roo.isStrict){
11655                 var ib = this.el.child('button');
11656                 if(ib && ib.getWidth() > 20){
11657                     ib.clip();
11658                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11659                 }
11660             }
11661             if(this.minWidth){
11662                 if(this.hidden){
11663                     this.el.beginMeasure();
11664                 }
11665                 if(this.el.getWidth() < this.minWidth){
11666                     this.el.setWidth(this.minWidth);
11667                 }
11668                 if(this.hidden){
11669                     this.el.endMeasure();
11670                 }
11671             }
11672         }
11673     },
11674
11675     /**
11676      * Assigns this button's click handler
11677      * @param {Function} handler The function to call when the button is clicked
11678      * @param {Object} scope (optional) Scope for the function passed in
11679      */
11680     setHandler : function(handler, scope){
11681         this.handler = handler;
11682         this.scope = scope;  
11683     },
11684     
11685     /**
11686      * Sets this button's text
11687      * @param {String} text The button text
11688      */
11689     setText : function(text){
11690         this.text = text;
11691         if(this.el){
11692             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11693         }
11694         this.autoWidth();
11695     },
11696     
11697     /**
11698      * Gets the text for this button
11699      * @return {String} The button text
11700      */
11701     getText : function(){
11702         return this.text;  
11703     },
11704     
11705     /**
11706      * Show this button
11707      */
11708     show: function(){
11709         this.hidden = false;
11710         if(this.el){
11711             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11712         }
11713     },
11714     
11715     /**
11716      * Hide this button
11717      */
11718     hide: function(){
11719         this.hidden = true;
11720         if(this.el){
11721             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11722         }
11723     },
11724     
11725     /**
11726      * Convenience function for boolean show/hide
11727      * @param {Boolean} visible True to show, false to hide
11728      */
11729     setVisible: function(visible){
11730         if(visible) {
11731             this.show();
11732         }else{
11733             this.hide();
11734         }
11735     },
11736     
11737     /**
11738      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11739      * @param {Boolean} state (optional) Force a particular state
11740      */
11741     toggle : function(state){
11742         state = state === undefined ? !this.pressed : state;
11743         if(state != this.pressed){
11744             if(state){
11745                 this.el.addClass("x-btn-pressed");
11746                 this.pressed = true;
11747                 this.fireEvent("toggle", this, true);
11748             }else{
11749                 this.el.removeClass("x-btn-pressed");
11750                 this.pressed = false;
11751                 this.fireEvent("toggle", this, false);
11752             }
11753             if(this.toggleHandler){
11754                 this.toggleHandler.call(this.scope || this, this, state);
11755             }
11756         }
11757     },
11758     
11759     /**
11760      * Focus the button
11761      */
11762     focus : function(){
11763         this.el.child('button:first').focus();
11764     },
11765     
11766     /**
11767      * Disable this button
11768      */
11769     disable : function(){
11770         if(this.el){
11771             this.el.addClass("x-btn-disabled");
11772         }
11773         this.disabled = true;
11774     },
11775     
11776     /**
11777      * Enable this button
11778      */
11779     enable : function(){
11780         if(this.el){
11781             this.el.removeClass("x-btn-disabled");
11782         }
11783         this.disabled = false;
11784     },
11785
11786     /**
11787      * Convenience function for boolean enable/disable
11788      * @param {Boolean} enabled True to enable, false to disable
11789      */
11790     setDisabled : function(v){
11791         this[v !== true ? "enable" : "disable"]();
11792     },
11793
11794     // private
11795     onClick : function(e){
11796         if(e){
11797             e.preventDefault();
11798         }
11799         if(e.button != 0){
11800             return;
11801         }
11802         if(!this.disabled){
11803             if(this.enableToggle){
11804                 this.toggle();
11805             }
11806             if(this.menu && !this.menu.isVisible()){
11807                 this.menu.show(this.el, this.menuAlign);
11808             }
11809             this.fireEvent("click", this, e);
11810             if(this.handler){
11811                 this.el.removeClass("x-btn-over");
11812                 this.handler.call(this.scope || this, this, e);
11813             }
11814         }
11815     },
11816     // private
11817     onMouseOver : function(e){
11818         if(!this.disabled){
11819             this.el.addClass("x-btn-over");
11820             this.fireEvent('mouseover', this, e);
11821         }
11822     },
11823     // private
11824     onMouseOut : function(e){
11825         if(!e.within(this.el,  true)){
11826             this.el.removeClass("x-btn-over");
11827             this.fireEvent('mouseout', this, e);
11828         }
11829     },
11830     // private
11831     onFocus : function(e){
11832         if(!this.disabled){
11833             this.el.addClass("x-btn-focus");
11834         }
11835     },
11836     // private
11837     onBlur : function(e){
11838         this.el.removeClass("x-btn-focus");
11839     },
11840     // private
11841     onMouseDown : function(e){
11842         if(!this.disabled && e.button == 0){
11843             this.el.addClass("x-btn-click");
11844             Roo.get(document).on('mouseup', this.onMouseUp, this);
11845         }
11846     },
11847     // private
11848     onMouseUp : function(e){
11849         if(e.button == 0){
11850             this.el.removeClass("x-btn-click");
11851             Roo.get(document).un('mouseup', this.onMouseUp, this);
11852         }
11853     },
11854     // private
11855     onMenuShow : function(e){
11856         this.el.addClass("x-btn-menu-active");
11857     },
11858     // private
11859     onMenuHide : function(e){
11860         this.el.removeClass("x-btn-menu-active");
11861     }   
11862 });
11863
11864 // Private utility class used by Button
11865 Roo.ButtonToggleMgr = function(){
11866    var groups = {};
11867    
11868    function toggleGroup(btn, state){
11869        if(state){
11870            var g = groups[btn.toggleGroup];
11871            for(var i = 0, l = g.length; i < l; i++){
11872                if(g[i] != btn){
11873                    g[i].toggle(false);
11874                }
11875            }
11876        }
11877    }
11878    
11879    return {
11880        register : function(btn){
11881            if(!btn.toggleGroup){
11882                return;
11883            }
11884            var g = groups[btn.toggleGroup];
11885            if(!g){
11886                g = groups[btn.toggleGroup] = [];
11887            }
11888            g.push(btn);
11889            btn.on("toggle", toggleGroup);
11890        },
11891        
11892        unregister : function(btn){
11893            if(!btn.toggleGroup){
11894                return;
11895            }
11896            var g = groups[btn.toggleGroup];
11897            if(g){
11898                g.remove(btn);
11899                btn.un("toggle", toggleGroup);
11900            }
11901        }
11902    };
11903 }();/*
11904  * Based on:
11905  * Ext JS Library 1.1.1
11906  * Copyright(c) 2006-2007, Ext JS, LLC.
11907  *
11908  * Originally Released Under LGPL - original licence link has changed is not relivant.
11909  *
11910  * Fork - LGPL
11911  * <script type="text/javascript">
11912  */
11913  
11914 /**
11915  * @class Roo.SplitButton
11916  * @extends Roo.Button
11917  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11918  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11919  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11920  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11921  * @cfg {String} arrowTooltip The title attribute of the arrow
11922  * @constructor
11923  * Create a new menu button
11924  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11925  * @param {Object} config The config object
11926  */
11927 Roo.SplitButton = function(renderTo, config){
11928     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11929     /**
11930      * @event arrowclick
11931      * Fires when this button's arrow is clicked
11932      * @param {SplitButton} this
11933      * @param {EventObject} e The click event
11934      */
11935     this.addEvents({"arrowclick":true});
11936 };
11937
11938 Roo.extend(Roo.SplitButton, Roo.Button, {
11939     render : function(renderTo){
11940         // this is one sweet looking template!
11941         var tpl = new Roo.Template(
11942             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11943             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11944             '<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>',
11945             "</tbody></table></td><td>",
11946             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11947             '<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>',
11948             "</tbody></table></td></tr></table>"
11949         );
11950         var btn = tpl.append(renderTo, [this.text, this.type], true);
11951         var btnEl = btn.child("button");
11952         if(this.cls){
11953             btn.addClass(this.cls);
11954         }
11955         if(this.icon){
11956             btnEl.setStyle('background-image', 'url(' +this.icon +')');
11957         }
11958         if(this.iconCls){
11959             btnEl.addClass(this.iconCls);
11960             if(!this.cls){
11961                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11962             }
11963         }
11964         this.el = btn;
11965         if(this.handleMouseEvents){
11966             btn.on("mouseover", this.onMouseOver, this);
11967             btn.on("mouseout", this.onMouseOut, this);
11968             btn.on("mousedown", this.onMouseDown, this);
11969             btn.on("mouseup", this.onMouseUp, this);
11970         }
11971         btn.on(this.clickEvent, this.onClick, this);
11972         if(this.tooltip){
11973             if(typeof this.tooltip == 'object'){
11974                 Roo.QuickTips.tips(Roo.apply({
11975                       target: btnEl.id
11976                 }, this.tooltip));
11977             } else {
11978                 btnEl.dom[this.tooltipType] = this.tooltip;
11979             }
11980         }
11981         if(this.arrowTooltip){
11982             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11983         }
11984         if(this.hidden){
11985             this.hide();
11986         }
11987         if(this.disabled){
11988             this.disable();
11989         }
11990         if(this.pressed){
11991             this.el.addClass("x-btn-pressed");
11992         }
11993         if(Roo.isIE && !Roo.isIE7){
11994             this.autoWidth.defer(1, this);
11995         }else{
11996             this.autoWidth();
11997         }
11998         if(this.menu){
11999             this.menu.on("show", this.onMenuShow, this);
12000             this.menu.on("hide", this.onMenuHide, this);
12001         }
12002         this.fireEvent('render', this);
12003     },
12004
12005     // private
12006     autoWidth : function(){
12007         if(this.el){
12008             var tbl = this.el.child("table:first");
12009             var tbl2 = this.el.child("table:last");
12010             this.el.setWidth("auto");
12011             tbl.setWidth("auto");
12012             if(Roo.isIE7 && Roo.isStrict){
12013                 var ib = this.el.child('button:first');
12014                 if(ib && ib.getWidth() > 20){
12015                     ib.clip();
12016                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12017                 }
12018             }
12019             if(this.minWidth){
12020                 if(this.hidden){
12021                     this.el.beginMeasure();
12022                 }
12023                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12024                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12025                 }
12026                 if(this.hidden){
12027                     this.el.endMeasure();
12028                 }
12029             }
12030             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12031         } 
12032     },
12033     /**
12034      * Sets this button's click handler
12035      * @param {Function} handler The function to call when the button is clicked
12036      * @param {Object} scope (optional) Scope for the function passed above
12037      */
12038     setHandler : function(handler, scope){
12039         this.handler = handler;
12040         this.scope = scope;  
12041     },
12042     
12043     /**
12044      * Sets this button's arrow click handler
12045      * @param {Function} handler The function to call when the arrow is clicked
12046      * @param {Object} scope (optional) Scope for the function passed above
12047      */
12048     setArrowHandler : function(handler, scope){
12049         this.arrowHandler = handler;
12050         this.scope = scope;  
12051     },
12052     
12053     /**
12054      * Focus the button
12055      */
12056     focus : function(){
12057         if(this.el){
12058             this.el.child("button:first").focus();
12059         }
12060     },
12061
12062     // private
12063     onClick : function(e){
12064         e.preventDefault();
12065         if(!this.disabled){
12066             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12067                 if(this.menu && !this.menu.isVisible()){
12068                     this.menu.show(this.el, this.menuAlign);
12069                 }
12070                 this.fireEvent("arrowclick", this, e);
12071                 if(this.arrowHandler){
12072                     this.arrowHandler.call(this.scope || this, this, e);
12073                 }
12074             }else{
12075                 this.fireEvent("click", this, e);
12076                 if(this.handler){
12077                     this.handler.call(this.scope || this, this, e);
12078                 }
12079             }
12080         }
12081     },
12082     // private
12083     onMouseDown : function(e){
12084         if(!this.disabled){
12085             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12086         }
12087     },
12088     // private
12089     onMouseUp : function(e){
12090         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12091     }   
12092 });
12093
12094
12095 // backwards compat
12096 Roo.MenuButton = Roo.SplitButton;/*
12097  * Based on:
12098  * Ext JS Library 1.1.1
12099  * Copyright(c) 2006-2007, Ext JS, LLC.
12100  *
12101  * Originally Released Under LGPL - original licence link has changed is not relivant.
12102  *
12103  * Fork - LGPL
12104  * <script type="text/javascript">
12105  */
12106
12107 /**
12108  * @class Roo.Toolbar
12109  * Basic Toolbar class.
12110  * @constructor
12111  * Creates a new Toolbar
12112  * @param {Object} config The config object
12113  */ 
12114 Roo.Toolbar = function(container, buttons, config)
12115 {
12116     /// old consturctor format still supported..
12117     if(container instanceof Array){ // omit the container for later rendering
12118         buttons = container;
12119         config = buttons;
12120         container = null;
12121     }
12122     if (typeof(container) == 'object' && container.xtype) {
12123         config = container;
12124         container = config.container;
12125         buttons = config.buttons; // not really - use items!!
12126     }
12127     var xitems = [];
12128     if (config && config.items) {
12129         xitems = config.items;
12130         delete config.items;
12131     }
12132     Roo.apply(this, config);
12133     this.buttons = buttons;
12134     
12135     if(container){
12136         this.render(container);
12137     }
12138     Roo.each(xitems, function(b) {
12139         this.add(b);
12140     }, this);
12141     
12142 };
12143
12144 Roo.Toolbar.prototype = {
12145     /**
12146      * @cfg {Roo.data.Store} items
12147      * array of button configs or elements to add
12148      */
12149     
12150     /**
12151      * @cfg {String/HTMLElement/Element} container
12152      * The id or element that will contain the toolbar
12153      */
12154     // private
12155     render : function(ct){
12156         this.el = Roo.get(ct);
12157         if(this.cls){
12158             this.el.addClass(this.cls);
12159         }
12160         // using a table allows for vertical alignment
12161         // 100% width is needed by Safari...
12162         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12163         this.tr = this.el.child("tr", true);
12164         var autoId = 0;
12165         this.items = new Roo.util.MixedCollection(false, function(o){
12166             return o.id || ("item" + (++autoId));
12167         });
12168         if(this.buttons){
12169             this.add.apply(this, this.buttons);
12170             delete this.buttons;
12171         }
12172     },
12173
12174     /**
12175      * Adds element(s) to the toolbar -- this function takes a variable number of 
12176      * arguments of mixed type and adds them to the toolbar.
12177      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12178      * <ul>
12179      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12180      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12181      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12182      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12183      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12184      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12185      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12186      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12187      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12188      * </ul>
12189      * @param {Mixed} arg2
12190      * @param {Mixed} etc.
12191      */
12192     add : function(){
12193         var a = arguments, l = a.length;
12194         for(var i = 0; i < l; i++){
12195             this._add(a[i]);
12196         }
12197     },
12198     // private..
12199     _add : function(el) {
12200         
12201         if (el.xtype) {
12202             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12203         }
12204         
12205         if (el.applyTo){ // some kind of form field
12206             return this.addField(el);
12207         } 
12208         if (el.render){ // some kind of Toolbar.Item
12209             return this.addItem(el);
12210         }
12211         if (typeof el == "string"){ // string
12212             if(el == "separator" || el == "-"){
12213                 return this.addSeparator();
12214             }
12215             if (el == " "){
12216                 return this.addSpacer();
12217             }
12218             if(el == "->"){
12219                 return this.addFill();
12220             }
12221             return this.addText(el);
12222             
12223         }
12224         if(el.tagName){ // element
12225             return this.addElement(el);
12226         }
12227         if(typeof el == "object"){ // must be button config?
12228             return this.addButton(el);
12229         }
12230         // and now what?!?!
12231         return false;
12232         
12233     },
12234     
12235     /**
12236      * Add an Xtype element
12237      * @param {Object} xtype Xtype Object
12238      * @return {Object} created Object
12239      */
12240     addxtype : function(e){
12241         return this.add(e);  
12242     },
12243     
12244     /**
12245      * Returns the Element for this toolbar.
12246      * @return {Roo.Element}
12247      */
12248     getEl : function(){
12249         return this.el;  
12250     },
12251     
12252     /**
12253      * Adds a separator
12254      * @return {Roo.Toolbar.Item} The separator item
12255      */
12256     addSeparator : function(){
12257         return this.addItem(new Roo.Toolbar.Separator());
12258     },
12259
12260     /**
12261      * Adds a spacer element
12262      * @return {Roo.Toolbar.Spacer} The spacer item
12263      */
12264     addSpacer : function(){
12265         return this.addItem(new Roo.Toolbar.Spacer());
12266     },
12267
12268     /**
12269      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12270      * @return {Roo.Toolbar.Fill} The fill item
12271      */
12272     addFill : function(){
12273         return this.addItem(new Roo.Toolbar.Fill());
12274     },
12275
12276     /**
12277      * Adds any standard HTML element to the toolbar
12278      * @param {String/HTMLElement/Element} el The element or id of the element to add
12279      * @return {Roo.Toolbar.Item} The element's item
12280      */
12281     addElement : function(el){
12282         return this.addItem(new Roo.Toolbar.Item(el));
12283     },
12284     /**
12285      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12286      * @type Roo.util.MixedCollection  
12287      */
12288     items : false,
12289      
12290     /**
12291      * Adds any Toolbar.Item or subclass
12292      * @param {Roo.Toolbar.Item} item
12293      * @return {Roo.Toolbar.Item} The item
12294      */
12295     addItem : function(item){
12296         var td = this.nextBlock();
12297         item.render(td);
12298         this.items.add(item);
12299         return item;
12300     },
12301     
12302     /**
12303      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12304      * @param {Object/Array} config A button config or array of configs
12305      * @return {Roo.Toolbar.Button/Array}
12306      */
12307     addButton : function(config){
12308         if(config instanceof Array){
12309             var buttons = [];
12310             for(var i = 0, len = config.length; i < len; i++) {
12311                 buttons.push(this.addButton(config[i]));
12312             }
12313             return buttons;
12314         }
12315         var b = config;
12316         if(!(config instanceof Roo.Toolbar.Button)){
12317             b = config.split ?
12318                 new Roo.Toolbar.SplitButton(config) :
12319                 new Roo.Toolbar.Button(config);
12320         }
12321         var td = this.nextBlock();
12322         b.render(td);
12323         this.items.add(b);
12324         return b;
12325     },
12326     
12327     /**
12328      * Adds text to the toolbar
12329      * @param {String} text The text to add
12330      * @return {Roo.Toolbar.Item} The element's item
12331      */
12332     addText : function(text){
12333         return this.addItem(new Roo.Toolbar.TextItem(text));
12334     },
12335     
12336     /**
12337      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12338      * @param {Number} index The index where the item is to be inserted
12339      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12340      * @return {Roo.Toolbar.Button/Item}
12341      */
12342     insertButton : function(index, item){
12343         if(item instanceof Array){
12344             var buttons = [];
12345             for(var i = 0, len = item.length; i < len; i++) {
12346                buttons.push(this.insertButton(index + i, item[i]));
12347             }
12348             return buttons;
12349         }
12350         if (!(item instanceof Roo.Toolbar.Button)){
12351            item = new Roo.Toolbar.Button(item);
12352         }
12353         var td = document.createElement("td");
12354         this.tr.insertBefore(td, this.tr.childNodes[index]);
12355         item.render(td);
12356         this.items.insert(index, item);
12357         return item;
12358     },
12359     
12360     /**
12361      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12362      * @param {Object} config
12363      * @return {Roo.Toolbar.Item} The element's item
12364      */
12365     addDom : function(config, returnEl){
12366         var td = this.nextBlock();
12367         Roo.DomHelper.overwrite(td, config);
12368         var ti = new Roo.Toolbar.Item(td.firstChild);
12369         ti.render(td);
12370         this.items.add(ti);
12371         return ti;
12372     },
12373
12374     /**
12375      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12376      * @type Roo.util.MixedCollection  
12377      */
12378     fields : false,
12379     
12380     /**
12381      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12382      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12383      * @param {Roo.form.Field} field
12384      * @return {Roo.ToolbarItem}
12385      */
12386      
12387       
12388     addField : function(field) {
12389         if (!this.fields) {
12390             var autoId = 0;
12391             this.fields = new Roo.util.MixedCollection(false, function(o){
12392                 return o.id || ("item" + (++autoId));
12393             });
12394
12395         }
12396         
12397         var td = this.nextBlock();
12398         field.render(td);
12399         var ti = new Roo.Toolbar.Item(td.firstChild);
12400         ti.render(td);
12401         this.items.add(ti);
12402         this.fields.add(field);
12403         return ti;
12404     },
12405     /**
12406      * Hide the toolbar
12407      * @method hide
12408      */
12409      
12410       
12411     hide : function()
12412     {
12413         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12414         this.el.child('div').hide();
12415     },
12416     /**
12417      * Show the toolbar
12418      * @method show
12419      */
12420     show : function()
12421     {
12422         this.el.child('div').show();
12423     },
12424       
12425     // private
12426     nextBlock : function(){
12427         var td = document.createElement("td");
12428         this.tr.appendChild(td);
12429         return td;
12430     },
12431
12432     // private
12433     destroy : function(){
12434         if(this.items){ // rendered?
12435             Roo.destroy.apply(Roo, this.items.items);
12436         }
12437         if(this.fields){ // rendered?
12438             Roo.destroy.apply(Roo, this.fields.items);
12439         }
12440         Roo.Element.uncache(this.el, this.tr);
12441     }
12442 };
12443
12444 /**
12445  * @class Roo.Toolbar.Item
12446  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12447  * @constructor
12448  * Creates a new Item
12449  * @param {HTMLElement} el 
12450  */
12451 Roo.Toolbar.Item = function(el){
12452     this.el = Roo.getDom(el);
12453     this.id = Roo.id(this.el);
12454     this.hidden = false;
12455 };
12456
12457 Roo.Toolbar.Item.prototype = {
12458     
12459     /**
12460      * Get this item's HTML Element
12461      * @return {HTMLElement}
12462      */
12463     getEl : function(){
12464        return this.el;  
12465     },
12466
12467     // private
12468     render : function(td){
12469         this.td = td;
12470         td.appendChild(this.el);
12471     },
12472     
12473     /**
12474      * Removes and destroys this item.
12475      */
12476     destroy : function(){
12477         this.td.parentNode.removeChild(this.td);
12478     },
12479     
12480     /**
12481      * Shows this item.
12482      */
12483     show: function(){
12484         this.hidden = false;
12485         this.td.style.display = "";
12486     },
12487     
12488     /**
12489      * Hides this item.
12490      */
12491     hide: function(){
12492         this.hidden = true;
12493         this.td.style.display = "none";
12494     },
12495     
12496     /**
12497      * Convenience function for boolean show/hide.
12498      * @param {Boolean} visible true to show/false to hide
12499      */
12500     setVisible: function(visible){
12501         if(visible) {
12502             this.show();
12503         }else{
12504             this.hide();
12505         }
12506     },
12507     
12508     /**
12509      * Try to focus this item.
12510      */
12511     focus : function(){
12512         Roo.fly(this.el).focus();
12513     },
12514     
12515     /**
12516      * Disables this item.
12517      */
12518     disable : function(){
12519         Roo.fly(this.td).addClass("x-item-disabled");
12520         this.disabled = true;
12521         this.el.disabled = true;
12522     },
12523     
12524     /**
12525      * Enables this item.
12526      */
12527     enable : function(){
12528         Roo.fly(this.td).removeClass("x-item-disabled");
12529         this.disabled = false;
12530         this.el.disabled = false;
12531     }
12532 };
12533
12534
12535 /**
12536  * @class Roo.Toolbar.Separator
12537  * @extends Roo.Toolbar.Item
12538  * A simple toolbar separator class
12539  * @constructor
12540  * Creates a new Separator
12541  */
12542 Roo.Toolbar.Separator = function(){
12543     var s = document.createElement("span");
12544     s.className = "ytb-sep";
12545     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12546 };
12547 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12548     enable:Roo.emptyFn,
12549     disable:Roo.emptyFn,
12550     focus:Roo.emptyFn
12551 });
12552
12553 /**
12554  * @class Roo.Toolbar.Spacer
12555  * @extends Roo.Toolbar.Item
12556  * A simple element that adds extra horizontal space to a toolbar.
12557  * @constructor
12558  * Creates a new Spacer
12559  */
12560 Roo.Toolbar.Spacer = function(){
12561     var s = document.createElement("div");
12562     s.className = "ytb-spacer";
12563     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12564 };
12565 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12566     enable:Roo.emptyFn,
12567     disable:Roo.emptyFn,
12568     focus:Roo.emptyFn
12569 });
12570
12571 /**
12572  * @class Roo.Toolbar.Fill
12573  * @extends Roo.Toolbar.Spacer
12574  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12575  * @constructor
12576  * Creates a new Spacer
12577  */
12578 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12579     // private
12580     render : function(td){
12581         td.style.width = '100%';
12582         Roo.Toolbar.Fill.superclass.render.call(this, td);
12583     }
12584 });
12585
12586 /**
12587  * @class Roo.Toolbar.TextItem
12588  * @extends Roo.Toolbar.Item
12589  * A simple class that renders text directly into a toolbar.
12590  * @constructor
12591  * Creates a new TextItem
12592  * @param {String} text
12593  */
12594 Roo.Toolbar.TextItem = function(text){
12595     if (typeof(text) == 'object') {
12596         text = text.text;
12597     }
12598     var s = document.createElement("span");
12599     s.className = "ytb-text";
12600     s.innerHTML = text;
12601     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12602 };
12603 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12604     enable:Roo.emptyFn,
12605     disable:Roo.emptyFn,
12606     focus:Roo.emptyFn
12607 });
12608
12609 /**
12610  * @class Roo.Toolbar.Button
12611  * @extends Roo.Button
12612  * A button that renders into a toolbar.
12613  * @constructor
12614  * Creates a new Button
12615  * @param {Object} config A standard {@link Roo.Button} config object
12616  */
12617 Roo.Toolbar.Button = function(config){
12618     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12619 };
12620 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12621     render : function(td){
12622         this.td = td;
12623         Roo.Toolbar.Button.superclass.render.call(this, td);
12624     },
12625     
12626     /**
12627      * Removes and destroys this button
12628      */
12629     destroy : function(){
12630         Roo.Toolbar.Button.superclass.destroy.call(this);
12631         this.td.parentNode.removeChild(this.td);
12632     },
12633     
12634     /**
12635      * Shows this button
12636      */
12637     show: function(){
12638         this.hidden = false;
12639         this.td.style.display = "";
12640     },
12641     
12642     /**
12643      * Hides this button
12644      */
12645     hide: function(){
12646         this.hidden = true;
12647         this.td.style.display = "none";
12648     },
12649
12650     /**
12651      * Disables this item
12652      */
12653     disable : function(){
12654         Roo.fly(this.td).addClass("x-item-disabled");
12655         this.disabled = true;
12656     },
12657
12658     /**
12659      * Enables this item
12660      */
12661     enable : function(){
12662         Roo.fly(this.td).removeClass("x-item-disabled");
12663         this.disabled = false;
12664     }
12665 });
12666 // backwards compat
12667 Roo.ToolbarButton = Roo.Toolbar.Button;
12668
12669 /**
12670  * @class Roo.Toolbar.SplitButton
12671  * @extends Roo.SplitButton
12672  * A menu button that renders into a toolbar.
12673  * @constructor
12674  * Creates a new SplitButton
12675  * @param {Object} config A standard {@link Roo.SplitButton} config object
12676  */
12677 Roo.Toolbar.SplitButton = function(config){
12678     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12679 };
12680 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12681     render : function(td){
12682         this.td = td;
12683         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12684     },
12685     
12686     /**
12687      * Removes and destroys this button
12688      */
12689     destroy : function(){
12690         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12691         this.td.parentNode.removeChild(this.td);
12692     },
12693     
12694     /**
12695      * Shows this button
12696      */
12697     show: function(){
12698         this.hidden = false;
12699         this.td.style.display = "";
12700     },
12701     
12702     /**
12703      * Hides this button
12704      */
12705     hide: function(){
12706         this.hidden = true;
12707         this.td.style.display = "none";
12708     }
12709 });
12710
12711 // backwards compat
12712 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12713  * Based on:
12714  * Ext JS Library 1.1.1
12715  * Copyright(c) 2006-2007, Ext JS, LLC.
12716  *
12717  * Originally Released Under LGPL - original licence link has changed is not relivant.
12718  *
12719  * Fork - LGPL
12720  * <script type="text/javascript">
12721  */
12722  
12723 /**
12724  * @class Roo.PagingToolbar
12725  * @extends Roo.Toolbar
12726  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12727  * @constructor
12728  * Create a new PagingToolbar
12729  * @param {Object} config The config object
12730  */
12731 Roo.PagingToolbar = function(el, ds, config)
12732 {
12733     // old args format still supported... - xtype is prefered..
12734     if (typeof(el) == 'object' && el.xtype) {
12735         // created from xtype...
12736         config = el;
12737         ds = el.dataSource;
12738         el = config.container;
12739     }
12740     
12741     
12742     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12743     this.ds = ds;
12744     this.cursor = 0;
12745     this.renderButtons(this.el);
12746     this.bind(ds);
12747 };
12748
12749 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12750     /**
12751      * @cfg {Roo.data.Store} dataSource
12752      * The underlying data store providing the paged data
12753      */
12754     /**
12755      * @cfg {String/HTMLElement/Element} container
12756      * container The id or element that will contain the toolbar
12757      */
12758     /**
12759      * @cfg {Boolean} displayInfo
12760      * True to display the displayMsg (defaults to false)
12761      */
12762     /**
12763      * @cfg {Number} pageSize
12764      * The number of records to display per page (defaults to 20)
12765      */
12766     pageSize: 20,
12767     /**
12768      * @cfg {String} displayMsg
12769      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12770      */
12771     displayMsg : 'Displaying {0} - {1} of {2}',
12772     /**
12773      * @cfg {String} emptyMsg
12774      * The message to display when no records are found (defaults to "No data to display")
12775      */
12776     emptyMsg : 'No data to display',
12777     /**
12778      * Customizable piece of the default paging text (defaults to "Page")
12779      * @type String
12780      */
12781     beforePageText : "Page",
12782     /**
12783      * Customizable piece of the default paging text (defaults to "of %0")
12784      * @type String
12785      */
12786     afterPageText : "of {0}",
12787     /**
12788      * Customizable piece of the default paging text (defaults to "First Page")
12789      * @type String
12790      */
12791     firstText : "First Page",
12792     /**
12793      * Customizable piece of the default paging text (defaults to "Previous Page")
12794      * @type String
12795      */
12796     prevText : "Previous Page",
12797     /**
12798      * Customizable piece of the default paging text (defaults to "Next Page")
12799      * @type String
12800      */
12801     nextText : "Next Page",
12802     /**
12803      * Customizable piece of the default paging text (defaults to "Last Page")
12804      * @type String
12805      */
12806     lastText : "Last Page",
12807     /**
12808      * Customizable piece of the default paging text (defaults to "Refresh")
12809      * @type String
12810      */
12811     refreshText : "Refresh",
12812
12813     // private
12814     renderButtons : function(el){
12815         Roo.PagingToolbar.superclass.render.call(this, el);
12816         this.first = this.addButton({
12817             tooltip: this.firstText,
12818             cls: "x-btn-icon x-grid-page-first",
12819             disabled: true,
12820             handler: this.onClick.createDelegate(this, ["first"])
12821         });
12822         this.prev = this.addButton({
12823             tooltip: this.prevText,
12824             cls: "x-btn-icon x-grid-page-prev",
12825             disabled: true,
12826             handler: this.onClick.createDelegate(this, ["prev"])
12827         });
12828         //this.addSeparator();
12829         this.add(this.beforePageText);
12830         this.field = Roo.get(this.addDom({
12831            tag: "input",
12832            type: "text",
12833            size: "3",
12834            value: "1",
12835            cls: "x-grid-page-number"
12836         }).el);
12837         this.field.on("keydown", this.onPagingKeydown, this);
12838         this.field.on("focus", function(){this.dom.select();});
12839         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12840         this.field.setHeight(18);
12841         //this.addSeparator();
12842         this.next = this.addButton({
12843             tooltip: this.nextText,
12844             cls: "x-btn-icon x-grid-page-next",
12845             disabled: true,
12846             handler: this.onClick.createDelegate(this, ["next"])
12847         });
12848         this.last = this.addButton({
12849             tooltip: this.lastText,
12850             cls: "x-btn-icon x-grid-page-last",
12851             disabled: true,
12852             handler: this.onClick.createDelegate(this, ["last"])
12853         });
12854         //this.addSeparator();
12855         this.loading = this.addButton({
12856             tooltip: this.refreshText,
12857             cls: "x-btn-icon x-grid-loading",
12858             handler: this.onClick.createDelegate(this, ["refresh"])
12859         });
12860
12861         if(this.displayInfo){
12862             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12863         }
12864     },
12865
12866     // private
12867     updateInfo : function(){
12868         if(this.displayEl){
12869             var count = this.ds.getCount();
12870             var msg = count == 0 ?
12871                 this.emptyMsg :
12872                 String.format(
12873                     this.displayMsg,
12874                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12875                 );
12876             this.displayEl.update(msg);
12877         }
12878     },
12879
12880     // private
12881     onLoad : function(ds, r, o){
12882        this.cursor = o.params ? o.params.start : 0;
12883        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12884
12885        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12886        this.field.dom.value = ap;
12887        this.first.setDisabled(ap == 1);
12888        this.prev.setDisabled(ap == 1);
12889        this.next.setDisabled(ap == ps);
12890        this.last.setDisabled(ap == ps);
12891        this.loading.enable();
12892        this.updateInfo();
12893     },
12894
12895     // private
12896     getPageData : function(){
12897         var total = this.ds.getTotalCount();
12898         return {
12899             total : total,
12900             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12901             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12902         };
12903     },
12904
12905     // private
12906     onLoadError : function(){
12907         this.loading.enable();
12908     },
12909
12910     // private
12911     onPagingKeydown : function(e){
12912         var k = e.getKey();
12913         var d = this.getPageData();
12914         if(k == e.RETURN){
12915             var v = this.field.dom.value, pageNum;
12916             if(!v || isNaN(pageNum = parseInt(v, 10))){
12917                 this.field.dom.value = d.activePage;
12918                 return;
12919             }
12920             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12921             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12922             e.stopEvent();
12923         }
12924         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))
12925         {
12926           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12927           this.field.dom.value = pageNum;
12928           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12929           e.stopEvent();
12930         }
12931         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12932         {
12933           var v = this.field.dom.value, pageNum; 
12934           var increment = (e.shiftKey) ? 10 : 1;
12935           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12936             increment *= -1;
12937           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12938             this.field.dom.value = d.activePage;
12939             return;
12940           }
12941           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12942           {
12943             this.field.dom.value = parseInt(v, 10) + increment;
12944             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12945             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12946           }
12947           e.stopEvent();
12948         }
12949     },
12950
12951     // private
12952     beforeLoad : function(){
12953         if(this.loading){
12954             this.loading.disable();
12955         }
12956     },
12957
12958     // private
12959     onClick : function(which){
12960         var ds = this.ds;
12961         switch(which){
12962             case "first":
12963                 ds.load({params:{start: 0, limit: this.pageSize}});
12964             break;
12965             case "prev":
12966                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12967             break;
12968             case "next":
12969                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12970             break;
12971             case "last":
12972                 var total = ds.getTotalCount();
12973                 var extra = total % this.pageSize;
12974                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12975                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12976             break;
12977             case "refresh":
12978                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12979             break;
12980         }
12981     },
12982
12983     /**
12984      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12985      * @param {Roo.data.Store} store The data store to unbind
12986      */
12987     unbind : function(ds){
12988         ds.un("beforeload", this.beforeLoad, this);
12989         ds.un("load", this.onLoad, this);
12990         ds.un("loadexception", this.onLoadError, this);
12991         ds.un("remove", this.updateInfo, this);
12992         ds.un("add", this.updateInfo, this);
12993         this.ds = undefined;
12994     },
12995
12996     /**
12997      * Binds the paging toolbar to the specified {@link Roo.data.Store}
12998      * @param {Roo.data.Store} store The data store to bind
12999      */
13000     bind : function(ds){
13001         ds.on("beforeload", this.beforeLoad, this);
13002         ds.on("load", this.onLoad, this);
13003         ds.on("loadexception", this.onLoadError, this);
13004         ds.on("remove", this.updateInfo, this);
13005         ds.on("add", this.updateInfo, this);
13006         this.ds = ds;
13007     }
13008 });/*
13009  * Based on:
13010  * Ext JS Library 1.1.1
13011  * Copyright(c) 2006-2007, Ext JS, LLC.
13012  *
13013  * Originally Released Under LGPL - original licence link has changed is not relivant.
13014  *
13015  * Fork - LGPL
13016  * <script type="text/javascript">
13017  */
13018
13019 /**
13020  * @class Roo.Resizable
13021  * @extends Roo.util.Observable
13022  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13023  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13024  * 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
13025  * the element will be wrapped for you automatically.</p>
13026  * <p>Here is the list of valid resize handles:</p>
13027  * <pre>
13028 Value   Description
13029 ------  -------------------
13030  'n'     north
13031  's'     south
13032  'e'     east
13033  'w'     west
13034  'nw'    northwest
13035  'sw'    southwest
13036  'se'    southeast
13037  'ne'    northeast
13038  'all'   all
13039 </pre>
13040  * <p>Here's an example showing the creation of a typical Resizable:</p>
13041  * <pre><code>
13042 var resizer = new Roo.Resizable("element-id", {
13043     handles: 'all',
13044     minWidth: 200,
13045     minHeight: 100,
13046     maxWidth: 500,
13047     maxHeight: 400,
13048     pinned: true
13049 });
13050 resizer.on("resize", myHandler);
13051 </code></pre>
13052  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13053  * resizer.east.setDisplayed(false);</p>
13054  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13055  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13056  * resize operation's new size (defaults to [0, 0])
13057  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13058  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13059  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13060  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13061  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13062  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13063  * @cfg {Number} width The width of the element in pixels (defaults to null)
13064  * @cfg {Number} height The height of the element in pixels (defaults to null)
13065  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13066  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13067  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13068  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13069  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13070  * in favor of the handles config option (defaults to false)
13071  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13072  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13073  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13074  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13075  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13076  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13077  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13078  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13079  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13080  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13081  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13082  * @constructor
13083  * Create a new resizable component
13084  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13085  * @param {Object} config configuration options
13086   */
13087 Roo.Resizable = function(el, config){
13088     this.el = Roo.get(el);
13089
13090     if(config && config.wrap){
13091         config.resizeChild = this.el;
13092         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13093         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13094         this.el.setStyle("overflow", "hidden");
13095         this.el.setPositioning(config.resizeChild.getPositioning());
13096         config.resizeChild.clearPositioning();
13097         if(!config.width || !config.height){
13098             var csize = config.resizeChild.getSize();
13099             this.el.setSize(csize.width, csize.height);
13100         }
13101         if(config.pinned && !config.adjustments){
13102             config.adjustments = "auto";
13103         }
13104     }
13105
13106     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13107     this.proxy.unselectable();
13108     this.proxy.enableDisplayMode('block');
13109
13110     Roo.apply(this, config);
13111
13112     if(this.pinned){
13113         this.disableTrackOver = true;
13114         this.el.addClass("x-resizable-pinned");
13115     }
13116     // if the element isn't positioned, make it relative
13117     var position = this.el.getStyle("position");
13118     if(position != "absolute" && position != "fixed"){
13119         this.el.setStyle("position", "relative");
13120     }
13121     if(!this.handles){ // no handles passed, must be legacy style
13122         this.handles = 's,e,se';
13123         if(this.multiDirectional){
13124             this.handles += ',n,w';
13125         }
13126     }
13127     if(this.handles == "all"){
13128         this.handles = "n s e w ne nw se sw";
13129     }
13130     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13131     var ps = Roo.Resizable.positions;
13132     for(var i = 0, len = hs.length; i < len; i++){
13133         if(hs[i] && ps[hs[i]]){
13134             var pos = ps[hs[i]];
13135             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13136         }
13137     }
13138     // legacy
13139     this.corner = this.southeast;
13140
13141     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1){
13142         this.updateBox = true;
13143     }
13144
13145     this.activeHandle = null;
13146
13147     if(this.resizeChild){
13148         if(typeof this.resizeChild == "boolean"){
13149             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13150         }else{
13151             this.resizeChild = Roo.get(this.resizeChild, true);
13152         }
13153     }
13154
13155     if(this.adjustments == "auto"){
13156         var rc = this.resizeChild;
13157         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13158         if(rc && (hw || hn)){
13159             rc.position("relative");
13160             rc.setLeft(hw ? hw.el.getWidth() : 0);
13161             rc.setTop(hn ? hn.el.getHeight() : 0);
13162         }
13163         this.adjustments = [
13164             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13165             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13166         ];
13167     }
13168
13169     if(this.draggable){
13170         this.dd = this.dynamic ?
13171             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13172         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13173     }
13174
13175     // public events
13176     this.addEvents({
13177         /**
13178          * @event beforeresize
13179          * Fired before resize is allowed. Set enabled to false to cancel resize.
13180          * @param {Roo.Resizable} this
13181          * @param {Roo.EventObject} e The mousedown event
13182          */
13183         "beforeresize" : true,
13184         /**
13185          * @event resize
13186          * Fired after a resize.
13187          * @param {Roo.Resizable} this
13188          * @param {Number} width The new width
13189          * @param {Number} height The new height
13190          * @param {Roo.EventObject} e The mouseup event
13191          */
13192         "resize" : true
13193     });
13194
13195     if(this.width !== null && this.height !== null){
13196         this.resizeTo(this.width, this.height);
13197     }else{
13198         this.updateChildSize();
13199     }
13200     if(Roo.isIE){
13201         this.el.dom.style.zoom = 1;
13202     }
13203     Roo.Resizable.superclass.constructor.call(this);
13204 };
13205
13206 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13207         resizeChild : false,
13208         adjustments : [0, 0],
13209         minWidth : 5,
13210         minHeight : 5,
13211         maxWidth : 10000,
13212         maxHeight : 10000,
13213         enabled : true,
13214         animate : false,
13215         duration : .35,
13216         dynamic : false,
13217         handles : false,
13218         multiDirectional : false,
13219         disableTrackOver : false,
13220         easing : 'easeOutStrong',
13221         widthIncrement : 0,
13222         heightIncrement : 0,
13223         pinned : false,
13224         width : null,
13225         height : null,
13226         preserveRatio : false,
13227         transparent: false,
13228         minX: 0,
13229         minY: 0,
13230         draggable: false,
13231
13232         /**
13233          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13234          */
13235         constrainTo: undefined,
13236         /**
13237          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13238          */
13239         resizeRegion: undefined,
13240
13241
13242     /**
13243      * Perform a manual resize
13244      * @param {Number} width
13245      * @param {Number} height
13246      */
13247     resizeTo : function(width, height){
13248         this.el.setSize(width, height);
13249         this.updateChildSize();
13250         this.fireEvent("resize", this, width, height, null);
13251     },
13252
13253     // private
13254     startSizing : function(e, handle){
13255         this.fireEvent("beforeresize", this, e);
13256         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13257
13258             if(!this.overlay){
13259                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13260                 this.overlay.unselectable();
13261                 this.overlay.enableDisplayMode("block");
13262                 this.overlay.on("mousemove", this.onMouseMove, this);
13263                 this.overlay.on("mouseup", this.onMouseUp, this);
13264             }
13265             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13266
13267             this.resizing = true;
13268             this.startBox = this.el.getBox();
13269             this.startPoint = e.getXY();
13270             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13271                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13272
13273             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13274             this.overlay.show();
13275
13276             if(this.constrainTo) {
13277                 var ct = Roo.get(this.constrainTo);
13278                 this.resizeRegion = ct.getRegion().adjust(
13279                     ct.getFrameWidth('t'),
13280                     ct.getFrameWidth('l'),
13281                     -ct.getFrameWidth('b'),
13282                     -ct.getFrameWidth('r')
13283                 );
13284             }
13285
13286             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13287             this.proxy.show();
13288             this.proxy.setBox(this.startBox);
13289             if(!this.dynamic){
13290                 this.proxy.setStyle('visibility', 'visible');
13291             }
13292         }
13293     },
13294
13295     // private
13296     onMouseDown : function(handle, e){
13297         if(this.enabled){
13298             e.stopEvent();
13299             this.activeHandle = handle;
13300             this.startSizing(e, handle);
13301         }
13302     },
13303
13304     // private
13305     onMouseUp : function(e){
13306         var size = this.resizeElement();
13307         this.resizing = false;
13308         this.handleOut();
13309         this.overlay.hide();
13310         this.proxy.hide();
13311         this.fireEvent("resize", this, size.width, size.height, e);
13312     },
13313
13314     // private
13315     updateChildSize : function(){
13316         if(this.resizeChild){
13317             var el = this.el;
13318             var child = this.resizeChild;
13319             var adj = this.adjustments;
13320             if(el.dom.offsetWidth){
13321                 var b = el.getSize(true);
13322                 child.setSize(b.width+adj[0], b.height+adj[1]);
13323             }
13324             // Second call here for IE
13325             // The first call enables instant resizing and
13326             // the second call corrects scroll bars if they
13327             // exist
13328             if(Roo.isIE){
13329                 setTimeout(function(){
13330                     if(el.dom.offsetWidth){
13331                         var b = el.getSize(true);
13332                         child.setSize(b.width+adj[0], b.height+adj[1]);
13333                     }
13334                 }, 10);
13335             }
13336         }
13337     },
13338
13339     // private
13340     snap : function(value, inc, min){
13341         if(!inc || !value) return value;
13342         var newValue = value;
13343         var m = value % inc;
13344         if(m > 0){
13345             if(m > (inc/2)){
13346                 newValue = value + (inc-m);
13347             }else{
13348                 newValue = value - m;
13349             }
13350         }
13351         return Math.max(min, newValue);
13352     },
13353
13354     // private
13355     resizeElement : function(){
13356         var box = this.proxy.getBox();
13357         if(this.updateBox){
13358             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13359         }else{
13360             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13361         }
13362         this.updateChildSize();
13363         if(!this.dynamic){
13364             this.proxy.hide();
13365         }
13366         return box;
13367     },
13368
13369     // private
13370     constrain : function(v, diff, m, mx){
13371         if(v - diff < m){
13372             diff = v - m;
13373         }else if(v - diff > mx){
13374             diff = mx - v;
13375         }
13376         return diff;
13377     },
13378
13379     // private
13380     onMouseMove : function(e){
13381         if(this.enabled){
13382             try{// try catch so if something goes wrong the user doesn't get hung
13383
13384             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13385                 return;
13386             }
13387
13388             //var curXY = this.startPoint;
13389             var curSize = this.curSize || this.startBox;
13390             var x = this.startBox.x, y = this.startBox.y;
13391             var ox = x, oy = y;
13392             var w = curSize.width, h = curSize.height;
13393             var ow = w, oh = h;
13394             var mw = this.minWidth, mh = this.minHeight;
13395             var mxw = this.maxWidth, mxh = this.maxHeight;
13396             var wi = this.widthIncrement;
13397             var hi = this.heightIncrement;
13398
13399             var eventXY = e.getXY();
13400             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13401             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13402
13403             var pos = this.activeHandle.position;
13404
13405             switch(pos){
13406                 case "east":
13407                     w += diffX;
13408                     w = Math.min(Math.max(mw, w), mxw);
13409                     break;
13410                 case "south":
13411                     h += diffY;
13412                     h = Math.min(Math.max(mh, h), mxh);
13413                     break;
13414                 case "southeast":
13415                     w += diffX;
13416                     h += diffY;
13417                     w = Math.min(Math.max(mw, w), mxw);
13418                     h = Math.min(Math.max(mh, h), mxh);
13419                     break;
13420                 case "north":
13421                     diffY = this.constrain(h, diffY, mh, mxh);
13422                     y += diffY;
13423                     h -= diffY;
13424                     break;
13425                 case "west":
13426                     diffX = this.constrain(w, diffX, mw, mxw);
13427                     x += diffX;
13428                     w -= diffX;
13429                     break;
13430                 case "northeast":
13431                     w += diffX;
13432                     w = Math.min(Math.max(mw, w), mxw);
13433                     diffY = this.constrain(h, diffY, mh, mxh);
13434                     y += diffY;
13435                     h -= diffY;
13436                     break;
13437                 case "northwest":
13438                     diffX = this.constrain(w, diffX, mw, mxw);
13439                     diffY = this.constrain(h, diffY, mh, mxh);
13440                     y += diffY;
13441                     h -= diffY;
13442                     x += diffX;
13443                     w -= diffX;
13444                     break;
13445                case "southwest":
13446                     diffX = this.constrain(w, diffX, mw, mxw);
13447                     h += diffY;
13448                     h = Math.min(Math.max(mh, h), mxh);
13449                     x += diffX;
13450                     w -= diffX;
13451                     break;
13452             }
13453
13454             var sw = this.snap(w, wi, mw);
13455             var sh = this.snap(h, hi, mh);
13456             if(sw != w || sh != h){
13457                 switch(pos){
13458                     case "northeast":
13459                         y -= sh - h;
13460                     break;
13461                     case "north":
13462                         y -= sh - h;
13463                         break;
13464                     case "southwest":
13465                         x -= sw - w;
13466                     break;
13467                     case "west":
13468                         x -= sw - w;
13469                         break;
13470                     case "northwest":
13471                         x -= sw - w;
13472                         y -= sh - h;
13473                     break;
13474                 }
13475                 w = sw;
13476                 h = sh;
13477             }
13478
13479             if(this.preserveRatio){
13480                 switch(pos){
13481                     case "southeast":
13482                     case "east":
13483                         h = oh * (w/ow);
13484                         h = Math.min(Math.max(mh, h), mxh);
13485                         w = ow * (h/oh);
13486                        break;
13487                     case "south":
13488                         w = ow * (h/oh);
13489                         w = Math.min(Math.max(mw, w), mxw);
13490                         h = oh * (w/ow);
13491                         break;
13492                     case "northeast":
13493                         w = ow * (h/oh);
13494                         w = Math.min(Math.max(mw, w), mxw);
13495                         h = oh * (w/ow);
13496                     break;
13497                     case "north":
13498                         var tw = w;
13499                         w = ow * (h/oh);
13500                         w = Math.min(Math.max(mw, w), mxw);
13501                         h = oh * (w/ow);
13502                         x += (tw - w) / 2;
13503                         break;
13504                     case "southwest":
13505                         h = oh * (w/ow);
13506                         h = Math.min(Math.max(mh, h), mxh);
13507                         var tw = w;
13508                         w = ow * (h/oh);
13509                         x += tw - w;
13510                         break;
13511                     case "west":
13512                         var th = h;
13513                         h = oh * (w/ow);
13514                         h = Math.min(Math.max(mh, h), mxh);
13515                         y += (th - h) / 2;
13516                         var tw = w;
13517                         w = ow * (h/oh);
13518                         x += tw - w;
13519                        break;
13520                     case "northwest":
13521                         var tw = w;
13522                         var th = h;
13523                         h = oh * (w/ow);
13524                         h = Math.min(Math.max(mh, h), mxh);
13525                         w = ow * (h/oh);
13526                         y += th - h;
13527                          x += tw - w;
13528                        break;
13529
13530                 }
13531             }
13532             this.proxy.setBounds(x, y, w, h);
13533             if(this.dynamic){
13534                 this.resizeElement();
13535             }
13536             }catch(e){}
13537         }
13538     },
13539
13540     // private
13541     handleOver : function(){
13542         if(this.enabled){
13543             this.el.addClass("x-resizable-over");
13544         }
13545     },
13546
13547     // private
13548     handleOut : function(){
13549         if(!this.resizing){
13550             this.el.removeClass("x-resizable-over");
13551         }
13552     },
13553
13554     /**
13555      * Returns the element this component is bound to.
13556      * @return {Roo.Element}
13557      */
13558     getEl : function(){
13559         return this.el;
13560     },
13561
13562     /**
13563      * Returns the resizeChild element (or null).
13564      * @return {Roo.Element}
13565      */
13566     getResizeChild : function(){
13567         return this.resizeChild;
13568     },
13569
13570     /**
13571      * Destroys this resizable. If the element was wrapped and
13572      * removeEl is not true then the element remains.
13573      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13574      */
13575     destroy : function(removeEl){
13576         this.proxy.remove();
13577         if(this.overlay){
13578             this.overlay.removeAllListeners();
13579             this.overlay.remove();
13580         }
13581         var ps = Roo.Resizable.positions;
13582         for(var k in ps){
13583             if(typeof ps[k] != "function" && this[ps[k]]){
13584                 var h = this[ps[k]];
13585                 h.el.removeAllListeners();
13586                 h.el.remove();
13587             }
13588         }
13589         if(removeEl){
13590             this.el.update("");
13591             this.el.remove();
13592         }
13593     }
13594 });
13595
13596 // private
13597 // hash to map config positions to true positions
13598 Roo.Resizable.positions = {
13599     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast"
13600 };
13601
13602 // private
13603 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13604     if(!this.tpl){
13605         // only initialize the template if resizable is used
13606         var tpl = Roo.DomHelper.createTemplate(
13607             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13608         );
13609         tpl.compile();
13610         Roo.Resizable.Handle.prototype.tpl = tpl;
13611     }
13612     this.position = pos;
13613     this.rz = rz;
13614     this.el = this.tpl.append(rz.el.dom, [this.position], true);
13615     this.el.unselectable();
13616     if(transparent){
13617         this.el.setOpacity(0);
13618     }
13619     this.el.on("mousedown", this.onMouseDown, this);
13620     if(!disableTrackOver){
13621         this.el.on("mouseover", this.onMouseOver, this);
13622         this.el.on("mouseout", this.onMouseOut, this);
13623     }
13624 };
13625
13626 // private
13627 Roo.Resizable.Handle.prototype = {
13628     afterResize : function(rz){
13629         // do nothing
13630     },
13631     // private
13632     onMouseDown : function(e){
13633         this.rz.onMouseDown(this, e);
13634     },
13635     // private
13636     onMouseOver : function(e){
13637         this.rz.handleOver(this, e);
13638     },
13639     // private
13640     onMouseOut : function(e){
13641         this.rz.handleOut(this, e);
13642     }
13643 };/*
13644  * Based on:
13645  * Ext JS Library 1.1.1
13646  * Copyright(c) 2006-2007, Ext JS, LLC.
13647  *
13648  * Originally Released Under LGPL - original licence link has changed is not relivant.
13649  *
13650  * Fork - LGPL
13651  * <script type="text/javascript">
13652  */
13653
13654 /**
13655  * @class Roo.Editor
13656  * @extends Roo.Component
13657  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13658  * @constructor
13659  * Create a new Editor
13660  * @param {Roo.form.Field} field The Field object (or descendant)
13661  * @param {Object} config The config object
13662  */
13663 Roo.Editor = function(field, config){
13664     Roo.Editor.superclass.constructor.call(this, config);
13665     this.field = field;
13666     this.addEvents({
13667         /**
13668              * @event beforestartedit
13669              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13670              * false from the handler of this event.
13671              * @param {Editor} this
13672              * @param {Roo.Element} boundEl The underlying element bound to this editor
13673              * @param {Mixed} value The field value being set
13674              */
13675         "beforestartedit" : true,
13676         /**
13677              * @event startedit
13678              * Fires when this editor is displayed
13679              * @param {Roo.Element} boundEl The underlying element bound to this editor
13680              * @param {Mixed} value The starting field value
13681              */
13682         "startedit" : true,
13683         /**
13684              * @event beforecomplete
13685              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13686              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13687              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13688              * event will not fire since no edit actually occurred.
13689              * @param {Editor} this
13690              * @param {Mixed} value The current field value
13691              * @param {Mixed} startValue The original field value
13692              */
13693         "beforecomplete" : true,
13694         /**
13695              * @event complete
13696              * Fires after editing is complete and any changed value has been written to the underlying field.
13697              * @param {Editor} this
13698              * @param {Mixed} value The current field value
13699              * @param {Mixed} startValue The original field value
13700              */
13701         "complete" : true,
13702         /**
13703          * @event specialkey
13704          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13705          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13706          * @param {Roo.form.Field} this
13707          * @param {Roo.EventObject} e The event object
13708          */
13709         "specialkey" : true
13710     });
13711 };
13712
13713 Roo.extend(Roo.Editor, Roo.Component, {
13714     /**
13715      * @cfg {Boolean/String} autosize
13716      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13717      * or "height" to adopt the height only (defaults to false)
13718      */
13719     /**
13720      * @cfg {Boolean} revertInvalid
13721      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13722      * validation fails (defaults to true)
13723      */
13724     /**
13725      * @cfg {Boolean} ignoreNoChange
13726      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13727      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13728      * will never be ignored.
13729      */
13730     /**
13731      * @cfg {Boolean} hideEl
13732      * False to keep the bound element visible while the editor is displayed (defaults to true)
13733      */
13734     /**
13735      * @cfg {Mixed} value
13736      * The data value of the underlying field (defaults to "")
13737      */
13738     value : "",
13739     /**
13740      * @cfg {String} alignment
13741      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13742      */
13743     alignment: "c-c?",
13744     /**
13745      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13746      * for bottom-right shadow (defaults to "frame")
13747      */
13748     shadow : "frame",
13749     /**
13750      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13751      */
13752     constrain : false,
13753     /**
13754      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13755      */
13756     completeOnEnter : false,
13757     /**
13758      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13759      */
13760     cancelOnEsc : false,
13761     /**
13762      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13763      */
13764     updateEl : false,
13765
13766     // private
13767     onRender : function(ct, position){
13768         this.el = new Roo.Layer({
13769             shadow: this.shadow,
13770             cls: "x-editor",
13771             parentEl : ct,
13772             shim : this.shim,
13773             shadowOffset:4,
13774             id: this.id,
13775             constrain: this.constrain
13776         });
13777         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13778         if(this.field.msgTarget != 'title'){
13779             this.field.msgTarget = 'qtip';
13780         }
13781         this.field.render(this.el);
13782         if(Roo.isGecko){
13783             this.field.el.dom.setAttribute('autocomplete', 'off');
13784         }
13785         this.field.on("specialkey", this.onSpecialKey, this);
13786         if(this.swallowKeys){
13787             this.field.el.swallowEvent(['keydown','keypress']);
13788         }
13789         this.field.show();
13790         this.field.on("blur", this.onBlur, this);
13791         if(this.field.grow){
13792             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13793         }
13794     },
13795
13796     onSpecialKey : function(field, e){
13797         //Roo.log('editor onSpecialKey');
13798         if(this.completeOnEnter && e.getKey() == e.ENTER){
13799             e.stopEvent();
13800             this.completeEdit();
13801         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
13802             this.cancelEdit();
13803         }else{
13804             this.fireEvent('specialkey', field, e);
13805         }
13806     },
13807
13808     /**
13809      * Starts the editing process and shows the editor.
13810      * @param {String/HTMLElement/Element} el The element to edit
13811      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13812       * to the innerHTML of el.
13813      */
13814     startEdit : function(el, value){
13815         if(this.editing){
13816             this.completeEdit();
13817         }
13818         this.boundEl = Roo.get(el);
13819         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13820         if(!this.rendered){
13821             this.render(this.parentEl || document.body);
13822         }
13823         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13824             return;
13825         }
13826         this.startValue = v;
13827         this.field.setValue(v);
13828         if(this.autoSize){
13829             var sz = this.boundEl.getSize();
13830             switch(this.autoSize){
13831                 case "width":
13832                 this.setSize(sz.width,  "");
13833                 break;
13834                 case "height":
13835                 this.setSize("",  sz.height);
13836                 break;
13837                 default:
13838                 this.setSize(sz.width,  sz.height);
13839             }
13840         }
13841         this.el.alignTo(this.boundEl, this.alignment);
13842         this.editing = true;
13843         if(Roo.QuickTips){
13844             Roo.QuickTips.disable();
13845         }
13846         this.show();
13847     },
13848
13849     /**
13850      * Sets the height and width of this editor.
13851      * @param {Number} width The new width
13852      * @param {Number} height The new height
13853      */
13854     setSize : function(w, h){
13855         this.field.setSize(w, h);
13856         if(this.el){
13857             this.el.sync();
13858         }
13859     },
13860
13861     /**
13862      * Realigns the editor to the bound field based on the current alignment config value.
13863      */
13864     realign : function(){
13865         this.el.alignTo(this.boundEl, this.alignment);
13866     },
13867
13868     /**
13869      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13870      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13871      */
13872     completeEdit : function(remainVisible){
13873         if(!this.editing){
13874             return;
13875         }
13876         var v = this.getValue();
13877         if(this.revertInvalid !== false && !this.field.isValid()){
13878             v = this.startValue;
13879             this.cancelEdit(true);
13880         }
13881         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13882             this.editing = false;
13883             this.hide();
13884             return;
13885         }
13886         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13887             this.editing = false;
13888             if(this.updateEl && this.boundEl){
13889                 this.boundEl.update(v);
13890             }
13891             if(remainVisible !== true){
13892                 this.hide();
13893             }
13894             this.fireEvent("complete", this, v, this.startValue);
13895         }
13896     },
13897
13898     // private
13899     onShow : function(){
13900         this.el.show();
13901         if(this.hideEl !== false){
13902             this.boundEl.hide();
13903         }
13904         this.field.show();
13905         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13906             this.fixIEFocus = true;
13907             this.deferredFocus.defer(50, this);
13908         }else{
13909             this.field.focus();
13910         }
13911         this.fireEvent("startedit", this.boundEl, this.startValue);
13912     },
13913
13914     deferredFocus : function(){
13915         if(this.editing){
13916             this.field.focus();
13917         }
13918     },
13919
13920     /**
13921      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13922      * reverted to the original starting value.
13923      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13924      * cancel (defaults to false)
13925      */
13926     cancelEdit : function(remainVisible){
13927         if(this.editing){
13928             this.setValue(this.startValue);
13929             if(remainVisible !== true){
13930                 this.hide();
13931             }
13932         }
13933     },
13934
13935     // private
13936     onBlur : function(){
13937         if(this.allowBlur !== true && this.editing){
13938             this.completeEdit();
13939         }
13940     },
13941
13942     // private
13943     onHide : function(){
13944         if(this.editing){
13945             this.completeEdit();
13946             return;
13947         }
13948         this.field.blur();
13949         if(this.field.collapse){
13950             this.field.collapse();
13951         }
13952         this.el.hide();
13953         if(this.hideEl !== false){
13954             this.boundEl.show();
13955         }
13956         if(Roo.QuickTips){
13957             Roo.QuickTips.enable();
13958         }
13959     },
13960
13961     /**
13962      * Sets the data value of the editor
13963      * @param {Mixed} value Any valid value supported by the underlying field
13964      */
13965     setValue : function(v){
13966         this.field.setValue(v);
13967     },
13968
13969     /**
13970      * Gets the data value of the editor
13971      * @return {Mixed} The data value
13972      */
13973     getValue : function(){
13974         return this.field.getValue();
13975     }
13976 });/*
13977  * Based on:
13978  * Ext JS Library 1.1.1
13979  * Copyright(c) 2006-2007, Ext JS, LLC.
13980  *
13981  * Originally Released Under LGPL - original licence link has changed is not relivant.
13982  *
13983  * Fork - LGPL
13984  * <script type="text/javascript">
13985  */
13986  
13987 /**
13988  * @class Roo.BasicDialog
13989  * @extends Roo.util.Observable
13990  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
13991  * <pre><code>
13992 var dlg = new Roo.BasicDialog("my-dlg", {
13993     height: 200,
13994     width: 300,
13995     minHeight: 100,
13996     minWidth: 150,
13997     modal: true,
13998     proxyDrag: true,
13999     shadow: true
14000 });
14001 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14002 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14003 dlg.addButton('Cancel', dlg.hide, dlg);
14004 dlg.show();
14005 </code></pre>
14006   <b>A Dialog should always be a direct child of the body element.</b>
14007  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14008  * @cfg {String} title Default text to display in the title bar (defaults to null)
14009  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14010  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14011  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14012  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14013  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14014  * (defaults to null with no animation)
14015  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14016  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14017  * property for valid values (defaults to 'all')
14018  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14019  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14020  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14021  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14022  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14023  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14024  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14025  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14026  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14027  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14028  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14029  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14030  * draggable = true (defaults to false)
14031  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14032  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14033  * shadow (defaults to false)
14034  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14035  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14036  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14037  * @cfg {Array} buttons Array of buttons
14038  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14039  * @constructor
14040  * Create a new BasicDialog.
14041  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14042  * @param {Object} config Configuration options
14043  */
14044 Roo.BasicDialog = function(el, config){
14045     this.el = Roo.get(el);
14046     var dh = Roo.DomHelper;
14047     if(!this.el && config && config.autoCreate){
14048         if(typeof config.autoCreate == "object"){
14049             if(!config.autoCreate.id){
14050                 config.autoCreate.id = el;
14051             }
14052             this.el = dh.append(document.body,
14053                         config.autoCreate, true);
14054         }else{
14055             this.el = dh.append(document.body,
14056                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14057         }
14058     }
14059     el = this.el;
14060     el.setDisplayed(true);
14061     el.hide = this.hideAction;
14062     this.id = el.id;
14063     el.addClass("x-dlg");
14064
14065     Roo.apply(this, config);
14066
14067     this.proxy = el.createProxy("x-dlg-proxy");
14068     this.proxy.hide = this.hideAction;
14069     this.proxy.setOpacity(.5);
14070     this.proxy.hide();
14071
14072     if(config.width){
14073         el.setWidth(config.width);
14074     }
14075     if(config.height){
14076         el.setHeight(config.height);
14077     }
14078     this.size = el.getSize();
14079     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14080         this.xy = [config.x,config.y];
14081     }else{
14082         this.xy = el.getCenterXY(true);
14083     }
14084     /** The header element @type Roo.Element */
14085     this.header = el.child("> .x-dlg-hd");
14086     /** The body element @type Roo.Element */
14087     this.body = el.child("> .x-dlg-bd");
14088     /** The footer element @type Roo.Element */
14089     this.footer = el.child("> .x-dlg-ft");
14090
14091     if(!this.header){
14092         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14093     }
14094     if(!this.body){
14095         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14096     }
14097
14098     this.header.unselectable();
14099     if(this.title){
14100         this.header.update(this.title);
14101     }
14102     // this element allows the dialog to be focused for keyboard event
14103     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14104     this.focusEl.swallowEvent("click", true);
14105
14106     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14107
14108     // wrap the body and footer for special rendering
14109     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14110     if(this.footer){
14111         this.bwrap.dom.appendChild(this.footer.dom);
14112     }
14113
14114     this.bg = this.el.createChild({
14115         tag: "div", cls:"x-dlg-bg",
14116         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14117     });
14118     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14119
14120
14121     if(this.autoScroll !== false && !this.autoTabs){
14122         this.body.setStyle("overflow", "auto");
14123     }
14124
14125     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14126
14127     if(this.closable !== false){
14128         this.el.addClass("x-dlg-closable");
14129         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14130         this.close.on("click", this.closeClick, this);
14131         this.close.addClassOnOver("x-dlg-close-over");
14132     }
14133     if(this.collapsible !== false){
14134         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14135         this.collapseBtn.on("click", this.collapseClick, this);
14136         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14137         this.header.on("dblclick", this.collapseClick, this);
14138     }
14139     if(this.resizable !== false){
14140         this.el.addClass("x-dlg-resizable");
14141         this.resizer = new Roo.Resizable(el, {
14142             minWidth: this.minWidth || 80,
14143             minHeight:this.minHeight || 80,
14144             handles: this.resizeHandles || "all",
14145             pinned: true
14146         });
14147         this.resizer.on("beforeresize", this.beforeResize, this);
14148         this.resizer.on("resize", this.onResize, this);
14149     }
14150     if(this.draggable !== false){
14151         el.addClass("x-dlg-draggable");
14152         if (!this.proxyDrag) {
14153             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14154         }
14155         else {
14156             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14157         }
14158         dd.setHandleElId(this.header.id);
14159         dd.endDrag = this.endMove.createDelegate(this);
14160         dd.startDrag = this.startMove.createDelegate(this);
14161         dd.onDrag = this.onDrag.createDelegate(this);
14162         dd.scroll = false;
14163         this.dd = dd;
14164     }
14165     if(this.modal){
14166         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14167         this.mask.enableDisplayMode("block");
14168         this.mask.hide();
14169         this.el.addClass("x-dlg-modal");
14170     }
14171     if(this.shadow){
14172         this.shadow = new Roo.Shadow({
14173             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14174             offset : this.shadowOffset
14175         });
14176     }else{
14177         this.shadowOffset = 0;
14178     }
14179     if(Roo.useShims && this.shim !== false){
14180         this.shim = this.el.createShim();
14181         this.shim.hide = this.hideAction;
14182         this.shim.hide();
14183     }else{
14184         this.shim = false;
14185     }
14186     if(this.autoTabs){
14187         this.initTabs();
14188     }
14189     if (this.buttons) { 
14190         var bts= this.buttons;
14191         this.buttons = [];
14192         Roo.each(bts, function(b) {
14193             this.addButton(b);
14194         }, this);
14195     }
14196     
14197     
14198     this.addEvents({
14199         /**
14200          * @event keydown
14201          * Fires when a key is pressed
14202          * @param {Roo.BasicDialog} this
14203          * @param {Roo.EventObject} e
14204          */
14205         "keydown" : true,
14206         /**
14207          * @event move
14208          * Fires when this dialog is moved by the user.
14209          * @param {Roo.BasicDialog} this
14210          * @param {Number} x The new page X
14211          * @param {Number} y The new page Y
14212          */
14213         "move" : true,
14214         /**
14215          * @event resize
14216          * Fires when this dialog is resized by the user.
14217          * @param {Roo.BasicDialog} this
14218          * @param {Number} width The new width
14219          * @param {Number} height The new height
14220          */
14221         "resize" : true,
14222         /**
14223          * @event beforehide
14224          * Fires before this dialog is hidden.
14225          * @param {Roo.BasicDialog} this
14226          */
14227         "beforehide" : true,
14228         /**
14229          * @event hide
14230          * Fires when this dialog is hidden.
14231          * @param {Roo.BasicDialog} this
14232          */
14233         "hide" : true,
14234         /**
14235          * @event beforeshow
14236          * Fires before this dialog is shown.
14237          * @param {Roo.BasicDialog} this
14238          */
14239         "beforeshow" : true,
14240         /**
14241          * @event show
14242          * Fires when this dialog is shown.
14243          * @param {Roo.BasicDialog} this
14244          */
14245         "show" : true
14246     });
14247     el.on("keydown", this.onKeyDown, this);
14248     el.on("mousedown", this.toFront, this);
14249     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14250     this.el.hide();
14251     Roo.DialogManager.register(this);
14252     Roo.BasicDialog.superclass.constructor.call(this);
14253 };
14254
14255 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14256     shadowOffset: Roo.isIE ? 6 : 5,
14257     minHeight: 80,
14258     minWidth: 200,
14259     minButtonWidth: 75,
14260     defaultButton: null,
14261     buttonAlign: "right",
14262     tabTag: 'div',
14263     firstShow: true,
14264
14265     /**
14266      * Sets the dialog title text
14267      * @param {String} text The title text to display
14268      * @return {Roo.BasicDialog} this
14269      */
14270     setTitle : function(text){
14271         this.header.update(text);
14272         return this;
14273     },
14274
14275     // private
14276     closeClick : function(){
14277         this.hide();
14278     },
14279
14280     // private
14281     collapseClick : function(){
14282         this[this.collapsed ? "expand" : "collapse"]();
14283     },
14284
14285     /**
14286      * Collapses the dialog to its minimized state (only the title bar is visible).
14287      * Equivalent to the user clicking the collapse dialog button.
14288      */
14289     collapse : function(){
14290         if(!this.collapsed){
14291             this.collapsed = true;
14292             this.el.addClass("x-dlg-collapsed");
14293             this.restoreHeight = this.el.getHeight();
14294             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14295         }
14296     },
14297
14298     /**
14299      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14300      * clicking the expand dialog button.
14301      */
14302     expand : function(){
14303         if(this.collapsed){
14304             this.collapsed = false;
14305             this.el.removeClass("x-dlg-collapsed");
14306             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14307         }
14308     },
14309
14310     /**
14311      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14312      * @return {Roo.TabPanel} The tabs component
14313      */
14314     initTabs : function(){
14315         var tabs = this.getTabs();
14316         while(tabs.getTab(0)){
14317             tabs.removeTab(0);
14318         }
14319         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14320             var dom = el.dom;
14321             tabs.addTab(Roo.id(dom), dom.title);
14322             dom.title = "";
14323         });
14324         tabs.activate(0);
14325         return tabs;
14326     },
14327
14328     // private
14329     beforeResize : function(){
14330         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14331     },
14332
14333     // private
14334     onResize : function(){
14335         this.refreshSize();
14336         this.syncBodyHeight();
14337         this.adjustAssets();
14338         this.focus();
14339         this.fireEvent("resize", this, this.size.width, this.size.height);
14340     },
14341
14342     // private
14343     onKeyDown : function(e){
14344         if(this.isVisible()){
14345             this.fireEvent("keydown", this, e);
14346         }
14347     },
14348
14349     /**
14350      * Resizes the dialog.
14351      * @param {Number} width
14352      * @param {Number} height
14353      * @return {Roo.BasicDialog} this
14354      */
14355     resizeTo : function(width, height){
14356         this.el.setSize(width, height);
14357         this.size = {width: width, height: height};
14358         this.syncBodyHeight();
14359         if(this.fixedcenter){
14360             this.center();
14361         }
14362         if(this.isVisible()){
14363             this.constrainXY();
14364             this.adjustAssets();
14365         }
14366         this.fireEvent("resize", this, width, height);
14367         return this;
14368     },
14369
14370
14371     /**
14372      * Resizes the dialog to fit the specified content size.
14373      * @param {Number} width
14374      * @param {Number} height
14375      * @return {Roo.BasicDialog} this
14376      */
14377     setContentSize : function(w, h){
14378         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14379         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14380         //if(!this.el.isBorderBox()){
14381             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14382             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14383         //}
14384         if(this.tabs){
14385             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14386             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14387         }
14388         this.resizeTo(w, h);
14389         return this;
14390     },
14391
14392     /**
14393      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14394      * executed in response to a particular key being pressed while the dialog is active.
14395      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14396      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14397      * @param {Function} fn The function to call
14398      * @param {Object} scope (optional) The scope of the function
14399      * @return {Roo.BasicDialog} this
14400      */
14401     addKeyListener : function(key, fn, scope){
14402         var keyCode, shift, ctrl, alt;
14403         if(typeof key == "object" && !(key instanceof Array)){
14404             keyCode = key["key"];
14405             shift = key["shift"];
14406             ctrl = key["ctrl"];
14407             alt = key["alt"];
14408         }else{
14409             keyCode = key;
14410         }
14411         var handler = function(dlg, e){
14412             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14413                 var k = e.getKey();
14414                 if(keyCode instanceof Array){
14415                     for(var i = 0, len = keyCode.length; i < len; i++){
14416                         if(keyCode[i] == k){
14417                           fn.call(scope || window, dlg, k, e);
14418                           return;
14419                         }
14420                     }
14421                 }else{
14422                     if(k == keyCode){
14423                         fn.call(scope || window, dlg, k, e);
14424                     }
14425                 }
14426             }
14427         };
14428         this.on("keydown", handler);
14429         return this;
14430     },
14431
14432     /**
14433      * Returns the TabPanel component (creates it if it doesn't exist).
14434      * Note: If you wish to simply check for the existence of tabs without creating them,
14435      * check for a null 'tabs' property.
14436      * @return {Roo.TabPanel} The tabs component
14437      */
14438     getTabs : function(){
14439         if(!this.tabs){
14440             this.el.addClass("x-dlg-auto-tabs");
14441             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14442             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14443         }
14444         return this.tabs;
14445     },
14446
14447     /**
14448      * Adds a button to the footer section of the dialog.
14449      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14450      * object or a valid Roo.DomHelper element config
14451      * @param {Function} handler The function called when the button is clicked
14452      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14453      * @return {Roo.Button} The new button
14454      */
14455     addButton : function(config, handler, scope){
14456         var dh = Roo.DomHelper;
14457         if(!this.footer){
14458             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14459         }
14460         if(!this.btnContainer){
14461             var tb = this.footer.createChild({
14462
14463                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14464                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14465             }, null, true);
14466             this.btnContainer = tb.firstChild.firstChild.firstChild;
14467         }
14468         var bconfig = {
14469             handler: handler,
14470             scope: scope,
14471             minWidth: this.minButtonWidth,
14472             hideParent:true
14473         };
14474         if(typeof config == "string"){
14475             bconfig.text = config;
14476         }else{
14477             if(config.tag){
14478                 bconfig.dhconfig = config;
14479             }else{
14480                 Roo.apply(bconfig, config);
14481             }
14482         }
14483         var fc = false;
14484         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14485             bconfig.position = Math.max(0, bconfig.position);
14486             fc = this.btnContainer.childNodes[bconfig.position];
14487         }
14488          
14489         var btn = new Roo.Button(
14490             fc ? 
14491                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14492                 : this.btnContainer.appendChild(document.createElement("td")),
14493             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14494             bconfig
14495         );
14496         this.syncBodyHeight();
14497         if(!this.buttons){
14498             /**
14499              * Array of all the buttons that have been added to this dialog via addButton
14500              * @type Array
14501              */
14502             this.buttons = [];
14503         }
14504         this.buttons.push(btn);
14505         return btn;
14506     },
14507
14508     /**
14509      * Sets the default button to be focused when the dialog is displayed.
14510      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14511      * @return {Roo.BasicDialog} this
14512      */
14513     setDefaultButton : function(btn){
14514         this.defaultButton = btn;
14515         return this;
14516     },
14517
14518     // private
14519     getHeaderFooterHeight : function(safe){
14520         var height = 0;
14521         if(this.header){
14522            height += this.header.getHeight();
14523         }
14524         if(this.footer){
14525            var fm = this.footer.getMargins();
14526             height += (this.footer.getHeight()+fm.top+fm.bottom);
14527         }
14528         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14529         height += this.centerBg.getPadding("tb");
14530         return height;
14531     },
14532
14533     // private
14534     syncBodyHeight : function(){
14535         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14536         var height = this.size.height - this.getHeaderFooterHeight(false);
14537         bd.setHeight(height-bd.getMargins("tb"));
14538         var hh = this.header.getHeight();
14539         var h = this.size.height-hh;
14540         cb.setHeight(h);
14541         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14542         bw.setHeight(h-cb.getPadding("tb"));
14543         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14544         bd.setWidth(bw.getWidth(true));
14545         if(this.tabs){
14546             this.tabs.syncHeight();
14547             if(Roo.isIE){
14548                 this.tabs.el.repaint();
14549             }
14550         }
14551     },
14552
14553     /**
14554      * Restores the previous state of the dialog if Roo.state is configured.
14555      * @return {Roo.BasicDialog} this
14556      */
14557     restoreState : function(){
14558         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14559         if(box && box.width){
14560             this.xy = [box.x, box.y];
14561             this.resizeTo(box.width, box.height);
14562         }
14563         return this;
14564     },
14565
14566     // private
14567     beforeShow : function(){
14568         this.expand();
14569         if(this.fixedcenter){
14570             this.xy = this.el.getCenterXY(true);
14571         }
14572         if(this.modal){
14573             Roo.get(document.body).addClass("x-body-masked");
14574             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14575             this.mask.show();
14576         }
14577         this.constrainXY();
14578     },
14579
14580     // private
14581     animShow : function(){
14582         var b = Roo.get(this.animateTarget, true).getBox();
14583         this.proxy.setSize(b.width, b.height);
14584         this.proxy.setLocation(b.x, b.y);
14585         this.proxy.show();
14586         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14587                     true, .35, this.showEl.createDelegate(this));
14588     },
14589
14590     /**
14591      * Shows the dialog.
14592      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14593      * @return {Roo.BasicDialog} this
14594      */
14595     show : function(animateTarget){
14596         if (this.fireEvent("beforeshow", this) === false){
14597             return;
14598         }
14599         if(this.syncHeightBeforeShow){
14600             this.syncBodyHeight();
14601         }else if(this.firstShow){
14602             this.firstShow = false;
14603             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14604         }
14605         this.animateTarget = animateTarget || this.animateTarget;
14606         if(!this.el.isVisible()){
14607             this.beforeShow();
14608             if(this.animateTarget){
14609                 this.animShow();
14610             }else{
14611                 this.showEl();
14612             }
14613         }
14614         return this;
14615     },
14616
14617     // private
14618     showEl : function(){
14619         this.proxy.hide();
14620         this.el.setXY(this.xy);
14621         this.el.show();
14622         this.adjustAssets(true);
14623         this.toFront();
14624         this.focus();
14625         // IE peekaboo bug - fix found by Dave Fenwick
14626         if(Roo.isIE){
14627             this.el.repaint();
14628         }
14629         this.fireEvent("show", this);
14630     },
14631
14632     /**
14633      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14634      * dialog itself will receive focus.
14635      */
14636     focus : function(){
14637         if(this.defaultButton){
14638             this.defaultButton.focus();
14639         }else{
14640             this.focusEl.focus();
14641         }
14642     },
14643
14644     // private
14645     constrainXY : function(){
14646         if(this.constraintoviewport !== false){
14647             if(!this.viewSize){
14648                 if(this.container){
14649                     var s = this.container.getSize();
14650                     this.viewSize = [s.width, s.height];
14651                 }else{
14652                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14653                 }
14654             }
14655             var s = Roo.get(this.container||document).getScroll();
14656
14657             var x = this.xy[0], y = this.xy[1];
14658             var w = this.size.width, h = this.size.height;
14659             var vw = this.viewSize[0], vh = this.viewSize[1];
14660             // only move it if it needs it
14661             var moved = false;
14662             // first validate right/bottom
14663             if(x + w > vw+s.left){
14664                 x = vw - w;
14665                 moved = true;
14666             }
14667             if(y + h > vh+s.top){
14668                 y = vh - h;
14669                 moved = true;
14670             }
14671             // then make sure top/left isn't negative
14672             if(x < s.left){
14673                 x = s.left;
14674                 moved = true;
14675             }
14676             if(y < s.top){
14677                 y = s.top;
14678                 moved = true;
14679             }
14680             if(moved){
14681                 // cache xy
14682                 this.xy = [x, y];
14683                 if(this.isVisible()){
14684                     this.el.setLocation(x, y);
14685                     this.adjustAssets();
14686                 }
14687             }
14688         }
14689     },
14690
14691     // private
14692     onDrag : function(){
14693         if(!this.proxyDrag){
14694             this.xy = this.el.getXY();
14695             this.adjustAssets();
14696         }
14697     },
14698
14699     // private
14700     adjustAssets : function(doShow){
14701         var x = this.xy[0], y = this.xy[1];
14702         var w = this.size.width, h = this.size.height;
14703         if(doShow === true){
14704             if(this.shadow){
14705                 this.shadow.show(this.el);
14706             }
14707             if(this.shim){
14708                 this.shim.show();
14709             }
14710         }
14711         if(this.shadow && this.shadow.isVisible()){
14712             this.shadow.show(this.el);
14713         }
14714         if(this.shim && this.shim.isVisible()){
14715             this.shim.setBounds(x, y, w, h);
14716         }
14717     },
14718
14719     // private
14720     adjustViewport : function(w, h){
14721         if(!w || !h){
14722             w = Roo.lib.Dom.getViewWidth();
14723             h = Roo.lib.Dom.getViewHeight();
14724         }
14725         // cache the size
14726         this.viewSize = [w, h];
14727         if(this.modal && this.mask.isVisible()){
14728             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14729             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14730         }
14731         if(this.isVisible()){
14732             this.constrainXY();
14733         }
14734     },
14735
14736     /**
14737      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14738      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14739      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14740      */
14741     destroy : function(removeEl){
14742         if(this.isVisible()){
14743             this.animateTarget = null;
14744             this.hide();
14745         }
14746         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14747         if(this.tabs){
14748             this.tabs.destroy(removeEl);
14749         }
14750         Roo.destroy(
14751              this.shim,
14752              this.proxy,
14753              this.resizer,
14754              this.close,
14755              this.mask
14756         );
14757         if(this.dd){
14758             this.dd.unreg();
14759         }
14760         if(this.buttons){
14761            for(var i = 0, len = this.buttons.length; i < len; i++){
14762                this.buttons[i].destroy();
14763            }
14764         }
14765         this.el.removeAllListeners();
14766         if(removeEl === true){
14767             this.el.update("");
14768             this.el.remove();
14769         }
14770         Roo.DialogManager.unregister(this);
14771     },
14772
14773     // private
14774     startMove : function(){
14775         if(this.proxyDrag){
14776             this.proxy.show();
14777         }
14778         if(this.constraintoviewport !== false){
14779             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14780         }
14781     },
14782
14783     // private
14784     endMove : function(){
14785         if(!this.proxyDrag){
14786             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14787         }else{
14788             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14789             this.proxy.hide();
14790         }
14791         this.refreshSize();
14792         this.adjustAssets();
14793         this.focus();
14794         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14795     },
14796
14797     /**
14798      * Brings this dialog to the front of any other visible dialogs
14799      * @return {Roo.BasicDialog} this
14800      */
14801     toFront : function(){
14802         Roo.DialogManager.bringToFront(this);
14803         return this;
14804     },
14805
14806     /**
14807      * Sends this dialog to the back (under) of any other visible dialogs
14808      * @return {Roo.BasicDialog} this
14809      */
14810     toBack : function(){
14811         Roo.DialogManager.sendToBack(this);
14812         return this;
14813     },
14814
14815     /**
14816      * Centers this dialog in the viewport
14817      * @return {Roo.BasicDialog} this
14818      */
14819     center : function(){
14820         var xy = this.el.getCenterXY(true);
14821         this.moveTo(xy[0], xy[1]);
14822         return this;
14823     },
14824
14825     /**
14826      * Moves the dialog's top-left corner to the specified point
14827      * @param {Number} x
14828      * @param {Number} y
14829      * @return {Roo.BasicDialog} this
14830      */
14831     moveTo : function(x, y){
14832         this.xy = [x,y];
14833         if(this.isVisible()){
14834             this.el.setXY(this.xy);
14835             this.adjustAssets();
14836         }
14837         return this;
14838     },
14839
14840     /**
14841      * Aligns the dialog to the specified element
14842      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14843      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14844      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14845      * @return {Roo.BasicDialog} this
14846      */
14847     alignTo : function(element, position, offsets){
14848         this.xy = this.el.getAlignToXY(element, position, offsets);
14849         if(this.isVisible()){
14850             this.el.setXY(this.xy);
14851             this.adjustAssets();
14852         }
14853         return this;
14854     },
14855
14856     /**
14857      * Anchors an element to another element and realigns it when the window is resized.
14858      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14859      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14860      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14861      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14862      * is a number, it is used as the buffer delay (defaults to 50ms).
14863      * @return {Roo.BasicDialog} this
14864      */
14865     anchorTo : function(el, alignment, offsets, monitorScroll){
14866         var action = function(){
14867             this.alignTo(el, alignment, offsets);
14868         };
14869         Roo.EventManager.onWindowResize(action, this);
14870         var tm = typeof monitorScroll;
14871         if(tm != 'undefined'){
14872             Roo.EventManager.on(window, 'scroll', action, this,
14873                 {buffer: tm == 'number' ? monitorScroll : 50});
14874         }
14875         action.call(this);
14876         return this;
14877     },
14878
14879     /**
14880      * Returns true if the dialog is visible
14881      * @return {Boolean}
14882      */
14883     isVisible : function(){
14884         return this.el.isVisible();
14885     },
14886
14887     // private
14888     animHide : function(callback){
14889         var b = Roo.get(this.animateTarget).getBox();
14890         this.proxy.show();
14891         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14892         this.el.hide();
14893         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14894                     this.hideEl.createDelegate(this, [callback]));
14895     },
14896
14897     /**
14898      * Hides the dialog.
14899      * @param {Function} callback (optional) Function to call when the dialog is hidden
14900      * @return {Roo.BasicDialog} this
14901      */
14902     hide : function(callback){
14903         if (this.fireEvent("beforehide", this) === false){
14904             return;
14905         }
14906         if(this.shadow){
14907             this.shadow.hide();
14908         }
14909         if(this.shim) {
14910           this.shim.hide();
14911         }
14912         if(this.animateTarget){
14913            this.animHide(callback);
14914         }else{
14915             this.el.hide();
14916             this.hideEl(callback);
14917         }
14918         return this;
14919     },
14920
14921     // private
14922     hideEl : function(callback){
14923         this.proxy.hide();
14924         if(this.modal){
14925             this.mask.hide();
14926             Roo.get(document.body).removeClass("x-body-masked");
14927         }
14928         this.fireEvent("hide", this);
14929         if(typeof callback == "function"){
14930             callback();
14931         }
14932     },
14933
14934     // private
14935     hideAction : function(){
14936         this.setLeft("-10000px");
14937         this.setTop("-10000px");
14938         this.setStyle("visibility", "hidden");
14939     },
14940
14941     // private
14942     refreshSize : function(){
14943         this.size = this.el.getSize();
14944         this.xy = this.el.getXY();
14945         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14946     },
14947
14948     // private
14949     // z-index is managed by the DialogManager and may be overwritten at any time
14950     setZIndex : function(index){
14951         if(this.modal){
14952             this.mask.setStyle("z-index", index);
14953         }
14954         if(this.shim){
14955             this.shim.setStyle("z-index", ++index);
14956         }
14957         if(this.shadow){
14958             this.shadow.setZIndex(++index);
14959         }
14960         this.el.setStyle("z-index", ++index);
14961         if(this.proxy){
14962             this.proxy.setStyle("z-index", ++index);
14963         }
14964         if(this.resizer){
14965             this.resizer.proxy.setStyle("z-index", ++index);
14966         }
14967
14968         this.lastZIndex = index;
14969     },
14970
14971     /**
14972      * Returns the element for this dialog
14973      * @return {Roo.Element} The underlying dialog Element
14974      */
14975     getEl : function(){
14976         return this.el;
14977     }
14978 });
14979
14980 /**
14981  * @class Roo.DialogManager
14982  * Provides global access to BasicDialogs that have been created and
14983  * support for z-indexing (layering) multiple open dialogs.
14984  */
14985 Roo.DialogManager = function(){
14986     var list = {};
14987     var accessList = [];
14988     var front = null;
14989
14990     // private
14991     var sortDialogs = function(d1, d2){
14992         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
14993     };
14994
14995     // private
14996     var orderDialogs = function(){
14997         accessList.sort(sortDialogs);
14998         var seed = Roo.DialogManager.zseed;
14999         for(var i = 0, len = accessList.length; i < len; i++){
15000             var dlg = accessList[i];
15001             if(dlg){
15002                 dlg.setZIndex(seed + (i*10));
15003             }
15004         }
15005     };
15006
15007     return {
15008         /**
15009          * The starting z-index for BasicDialogs (defaults to 9000)
15010          * @type Number The z-index value
15011          */
15012         zseed : 9000,
15013
15014         // private
15015         register : function(dlg){
15016             list[dlg.id] = dlg;
15017             accessList.push(dlg);
15018         },
15019
15020         // private
15021         unregister : function(dlg){
15022             delete list[dlg.id];
15023             var i=0;
15024             var len=0;
15025             if(!accessList.indexOf){
15026                 for(  i = 0, len = accessList.length; i < len; i++){
15027                     if(accessList[i] == dlg){
15028                         accessList.splice(i, 1);
15029                         return;
15030                     }
15031                 }
15032             }else{
15033                  i = accessList.indexOf(dlg);
15034                 if(i != -1){
15035                     accessList.splice(i, 1);
15036                 }
15037             }
15038         },
15039
15040         /**
15041          * Gets a registered dialog by id
15042          * @param {String/Object} id The id of the dialog or a dialog
15043          * @return {Roo.BasicDialog} this
15044          */
15045         get : function(id){
15046             return typeof id == "object" ? id : list[id];
15047         },
15048
15049         /**
15050          * Brings the specified dialog to the front
15051          * @param {String/Object} dlg The id of the dialog or a dialog
15052          * @return {Roo.BasicDialog} this
15053          */
15054         bringToFront : function(dlg){
15055             dlg = this.get(dlg);
15056             if(dlg != front){
15057                 front = dlg;
15058                 dlg._lastAccess = new Date().getTime();
15059                 orderDialogs();
15060             }
15061             return dlg;
15062         },
15063
15064         /**
15065          * Sends the specified dialog to the back
15066          * @param {String/Object} dlg The id of the dialog or a dialog
15067          * @return {Roo.BasicDialog} this
15068          */
15069         sendToBack : function(dlg){
15070             dlg = this.get(dlg);
15071             dlg._lastAccess = -(new Date().getTime());
15072             orderDialogs();
15073             return dlg;
15074         },
15075
15076         /**
15077          * Hides all dialogs
15078          */
15079         hideAll : function(){
15080             for(var id in list){
15081                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15082                     list[id].hide();
15083                 }
15084             }
15085         }
15086     };
15087 }();
15088
15089 /**
15090  * @class Roo.LayoutDialog
15091  * @extends Roo.BasicDialog
15092  * Dialog which provides adjustments for working with a layout in a Dialog.
15093  * Add your necessary layout config options to the dialog's config.<br>
15094  * Example usage (including a nested layout):
15095  * <pre><code>
15096 if(!dialog){
15097     dialog = new Roo.LayoutDialog("download-dlg", {
15098         modal: true,
15099         width:600,
15100         height:450,
15101         shadow:true,
15102         minWidth:500,
15103         minHeight:350,
15104         autoTabs:true,
15105         proxyDrag:true,
15106         // layout config merges with the dialog config
15107         center:{
15108             tabPosition: "top",
15109             alwaysShowTabs: true
15110         }
15111     });
15112     dialog.addKeyListener(27, dialog.hide, dialog);
15113     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15114     dialog.addButton("Build It!", this.getDownload, this);
15115
15116     // we can even add nested layouts
15117     var innerLayout = new Roo.BorderLayout("dl-inner", {
15118         east: {
15119             initialSize: 200,
15120             autoScroll:true,
15121             split:true
15122         },
15123         center: {
15124             autoScroll:true
15125         }
15126     });
15127     innerLayout.beginUpdate();
15128     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15129     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15130     innerLayout.endUpdate(true);
15131
15132     var layout = dialog.getLayout();
15133     layout.beginUpdate();
15134     layout.add("center", new Roo.ContentPanel("standard-panel",
15135                         {title: "Download the Source", fitToFrame:true}));
15136     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15137                {title: "Build your own roo.js"}));
15138     layout.getRegion("center").showPanel(sp);
15139     layout.endUpdate();
15140 }
15141 </code></pre>
15142     * @constructor
15143     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15144     * @param {Object} config configuration options
15145   */
15146 Roo.LayoutDialog = function(el, cfg){
15147     
15148     var config=  cfg;
15149     if (typeof(cfg) == 'undefined') {
15150         config = Roo.apply({}, el);
15151         // not sure why we use documentElement here.. - it should always be body.
15152         // IE7 borks horribly if we use documentElement.
15153         el = Roo.get( Roo.isIE ? (document.body || document.documentElement) : (document.documentElement || document.body) ).createChild();
15154         //config.autoCreate = true;
15155     }
15156     
15157     
15158     config.autoTabs = false;
15159     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15160     this.body.setStyle({overflow:"hidden", position:"relative"});
15161     this.layout = new Roo.BorderLayout(this.body.dom, config);
15162     this.layout.monitorWindowResize = false;
15163     this.el.addClass("x-dlg-auto-layout");
15164     // fix case when center region overwrites center function
15165     this.center = Roo.BasicDialog.prototype.center;
15166     this.on("show", this.layout.layout, this.layout, true);
15167     if (config.items) {
15168         var xitems = config.items;
15169         delete config.items;
15170         Roo.each(xitems, this.addxtype, this);
15171     }
15172     
15173     
15174 };
15175 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15176     /**
15177      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15178      * @deprecated
15179      */
15180     endUpdate : function(){
15181         this.layout.endUpdate();
15182     },
15183
15184     /**
15185      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15186      *  @deprecated
15187      */
15188     beginUpdate : function(){
15189         this.layout.beginUpdate();
15190     },
15191
15192     /**
15193      * Get the BorderLayout for this dialog
15194      * @return {Roo.BorderLayout}
15195      */
15196     getLayout : function(){
15197         return this.layout;
15198     },
15199
15200     showEl : function(){
15201         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15202         if(Roo.isIE7){
15203             this.layout.layout();
15204         }
15205     },
15206
15207     // private
15208     // Use the syncHeightBeforeShow config option to control this automatically
15209     syncBodyHeight : function(){
15210         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15211         if(this.layout){this.layout.layout();}
15212     },
15213     
15214       /**
15215      * Add an xtype element (actually adds to the layout.)
15216      * @return {Object} xdata xtype object data.
15217      */
15218     
15219     addxtype : function(c) {
15220         return this.layout.addxtype(c);
15221     }
15222 });/*
15223  * Based on:
15224  * Ext JS Library 1.1.1
15225  * Copyright(c) 2006-2007, Ext JS, LLC.
15226  *
15227  * Originally Released Under LGPL - original licence link has changed is not relivant.
15228  *
15229  * Fork - LGPL
15230  * <script type="text/javascript">
15231  */
15232  
15233 /**
15234  * @class Roo.MessageBox
15235  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15236  * Example usage:
15237  *<pre><code>
15238 // Basic alert:
15239 Roo.Msg.alert('Status', 'Changes saved successfully.');
15240
15241 // Prompt for user data:
15242 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15243     if (btn == 'ok'){
15244         // process text value...
15245     }
15246 });
15247
15248 // Show a dialog using config options:
15249 Roo.Msg.show({
15250    title:'Save Changes?',
15251    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15252    buttons: Roo.Msg.YESNOCANCEL,
15253    fn: processResult,
15254    animEl: 'elId'
15255 });
15256 </code></pre>
15257  * @singleton
15258  */
15259 Roo.MessageBox = function(){
15260     var dlg, opt, mask, waitTimer;
15261     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15262     var buttons, activeTextEl, bwidth;
15263
15264     // private
15265     var handleButton = function(button){
15266         dlg.hide();
15267         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15268     };
15269
15270     // private
15271     var handleHide = function(){
15272         if(opt && opt.cls){
15273             dlg.el.removeClass(opt.cls);
15274         }
15275         if(waitTimer){
15276             Roo.TaskMgr.stop(waitTimer);
15277             waitTimer = null;
15278         }
15279     };
15280
15281     // private
15282     var updateButtons = function(b){
15283         var width = 0;
15284         if(!b){
15285             buttons["ok"].hide();
15286             buttons["cancel"].hide();
15287             buttons["yes"].hide();
15288             buttons["no"].hide();
15289             dlg.footer.dom.style.display = 'none';
15290             return width;
15291         }
15292         dlg.footer.dom.style.display = '';
15293         for(var k in buttons){
15294             if(typeof buttons[k] != "function"){
15295                 if(b[k]){
15296                     buttons[k].show();
15297                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15298                     width += buttons[k].el.getWidth()+15;
15299                 }else{
15300                     buttons[k].hide();
15301                 }
15302             }
15303         }
15304         return width;
15305     };
15306
15307     // private
15308     var handleEsc = function(d, k, e){
15309         if(opt && opt.closable !== false){
15310             dlg.hide();
15311         }
15312         if(e){
15313             e.stopEvent();
15314         }
15315     };
15316
15317     return {
15318         /**
15319          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15320          * @return {Roo.BasicDialog} The BasicDialog element
15321          */
15322         getDialog : function(){
15323            if(!dlg){
15324                 dlg = new Roo.BasicDialog("x-msg-box", {
15325                     autoCreate : true,
15326                     shadow: true,
15327                     draggable: true,
15328                     resizable:false,
15329                     constraintoviewport:false,
15330                     fixedcenter:true,
15331                     collapsible : false,
15332                     shim:true,
15333                     modal: true,
15334                     width:400, height:100,
15335                     buttonAlign:"center",
15336                     closeClick : function(){
15337                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15338                             handleButton("no");
15339                         }else{
15340                             handleButton("cancel");
15341                         }
15342                     }
15343                 });
15344                 dlg.on("hide", handleHide);
15345                 mask = dlg.mask;
15346                 dlg.addKeyListener(27, handleEsc);
15347                 buttons = {};
15348                 var bt = this.buttonText;
15349                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15350                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15351                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15352                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15353                 bodyEl = dlg.body.createChild({
15354
15355                     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>'
15356                 });
15357                 msgEl = bodyEl.dom.firstChild;
15358                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15359                 textboxEl.enableDisplayMode();
15360                 textboxEl.addKeyListener([10,13], function(){
15361                     if(dlg.isVisible() && opt && opt.buttons){
15362                         if(opt.buttons.ok){
15363                             handleButton("ok");
15364                         }else if(opt.buttons.yes){
15365                             handleButton("yes");
15366                         }
15367                     }
15368                 });
15369                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15370                 textareaEl.enableDisplayMode();
15371                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15372                 progressEl.enableDisplayMode();
15373                 var pf = progressEl.dom.firstChild;
15374                 if (pf) {
15375                     pp = Roo.get(pf.firstChild);
15376                     pp.setHeight(pf.offsetHeight);
15377                 }
15378                 
15379             }
15380             return dlg;
15381         },
15382
15383         /**
15384          * Updates the message box body text
15385          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15386          * the XHTML-compliant non-breaking space character '&amp;#160;')
15387          * @return {Roo.MessageBox} This message box
15388          */
15389         updateText : function(text){
15390             if(!dlg.isVisible() && !opt.width){
15391                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15392             }
15393             msgEl.innerHTML = text || '&#160;';
15394             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15395                         Math.max(opt.minWidth || this.minWidth, bwidth));
15396             if(opt.prompt){
15397                 activeTextEl.setWidth(w);
15398             }
15399             if(dlg.isVisible()){
15400                 dlg.fixedcenter = false;
15401             }
15402             dlg.setContentSize(w, bodyEl.getHeight());
15403             if(dlg.isVisible()){
15404                 dlg.fixedcenter = true;
15405             }
15406             return this;
15407         },
15408
15409         /**
15410          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15411          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15412          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15413          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15414          * @return {Roo.MessageBox} This message box
15415          */
15416         updateProgress : function(value, text){
15417             if(text){
15418                 this.updateText(text);
15419             }
15420             if (pp) { // weird bug on my firefox - for some reason this is not defined
15421                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15422             }
15423             return this;
15424         },        
15425
15426         /**
15427          * Returns true if the message box is currently displayed
15428          * @return {Boolean} True if the message box is visible, else false
15429          */
15430         isVisible : function(){
15431             return dlg && dlg.isVisible();  
15432         },
15433
15434         /**
15435          * Hides the message box if it is displayed
15436          */
15437         hide : function(){
15438             if(this.isVisible()){
15439                 dlg.hide();
15440             }  
15441         },
15442
15443         /**
15444          * Displays a new message box, or reinitializes an existing message box, based on the config options
15445          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15446          * The following config object properties are supported:
15447          * <pre>
15448 Property    Type             Description
15449 ----------  ---------------  ------------------------------------------------------------------------------------
15450 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15451                                    closes (defaults to undefined)
15452 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15453                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15454 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15455                                    progress and wait dialogs will ignore this property and always hide the
15456                                    close button as they can only be closed programmatically.
15457 cls               String           A custom CSS class to apply to the message box element
15458 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15459                                    displayed (defaults to 75)
15460 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15461                                    function will be btn (the name of the button that was clicked, if applicable,
15462                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15463                                    Progress and wait dialogs will ignore this option since they do not respond to
15464                                    user actions and can only be closed programmatically, so any required function
15465                                    should be called by the same code after it closes the dialog.
15466 icon              String           A CSS class that provides a background image to be used as an icon for
15467                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15468 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15469 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15470 modal             Boolean          False to allow user interaction with the page while the message box is
15471                                    displayed (defaults to true)
15472 msg               String           A string that will replace the existing message box body text (defaults
15473                                    to the XHTML-compliant non-breaking space character '&#160;')
15474 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15475 progress          Boolean          True to display a progress bar (defaults to false)
15476 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15477 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15478 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15479 title             String           The title text
15480 value             String           The string value to set into the active textbox element if displayed
15481 wait              Boolean          True to display a progress bar (defaults to false)
15482 width             Number           The width of the dialog in pixels
15483 </pre>
15484          *
15485          * Example usage:
15486          * <pre><code>
15487 Roo.Msg.show({
15488    title: 'Address',
15489    msg: 'Please enter your address:',
15490    width: 300,
15491    buttons: Roo.MessageBox.OKCANCEL,
15492    multiline: true,
15493    fn: saveAddress,
15494    animEl: 'addAddressBtn'
15495 });
15496 </code></pre>
15497          * @param {Object} config Configuration options
15498          * @return {Roo.MessageBox} This message box
15499          */
15500         show : function(options){
15501             if(this.isVisible()){
15502                 this.hide();
15503             }
15504             var d = this.getDialog();
15505             opt = options;
15506             d.setTitle(opt.title || "&#160;");
15507             d.close.setDisplayed(opt.closable !== false);
15508             activeTextEl = textboxEl;
15509             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15510             if(opt.prompt){
15511                 if(opt.multiline){
15512                     textboxEl.hide();
15513                     textareaEl.show();
15514                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15515                         opt.multiline : this.defaultTextHeight);
15516                     activeTextEl = textareaEl;
15517                 }else{
15518                     textboxEl.show();
15519                     textareaEl.hide();
15520                 }
15521             }else{
15522                 textboxEl.hide();
15523                 textareaEl.hide();
15524             }
15525             progressEl.setDisplayed(opt.progress === true);
15526             this.updateProgress(0);
15527             activeTextEl.dom.value = opt.value || "";
15528             if(opt.prompt){
15529                 dlg.setDefaultButton(activeTextEl);
15530             }else{
15531                 var bs = opt.buttons;
15532                 var db = null;
15533                 if(bs && bs.ok){
15534                     db = buttons["ok"];
15535                 }else if(bs && bs.yes){
15536                     db = buttons["yes"];
15537                 }
15538                 dlg.setDefaultButton(db);
15539             }
15540             bwidth = updateButtons(opt.buttons);
15541             this.updateText(opt.msg);
15542             if(opt.cls){
15543                 d.el.addClass(opt.cls);
15544             }
15545             d.proxyDrag = opt.proxyDrag === true;
15546             d.modal = opt.modal !== false;
15547             d.mask = opt.modal !== false ? mask : false;
15548             if(!d.isVisible()){
15549                 // force it to the end of the z-index stack so it gets a cursor in FF
15550                 document.body.appendChild(dlg.el.dom);
15551                 d.animateTarget = null;
15552                 d.show(options.animEl);
15553             }
15554             return this;
15555         },
15556
15557         /**
15558          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15559          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15560          * and closing the message box when the process is complete.
15561          * @param {String} title The title bar text
15562          * @param {String} msg The message box body text
15563          * @return {Roo.MessageBox} This message box
15564          */
15565         progress : function(title, msg){
15566             this.show({
15567                 title : title,
15568                 msg : msg,
15569                 buttons: false,
15570                 progress:true,
15571                 closable:false,
15572                 minWidth: this.minProgressWidth,
15573                 modal : true
15574             });
15575             return this;
15576         },
15577
15578         /**
15579          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15580          * If a callback function is passed it will be called after the user clicks the button, and the
15581          * id of the button that was clicked will be passed as the only parameter to the callback
15582          * (could also be the top-right close button).
15583          * @param {String} title The title bar text
15584          * @param {String} msg The message box body text
15585          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15586          * @param {Object} scope (optional) The scope of the callback function
15587          * @return {Roo.MessageBox} This message box
15588          */
15589         alert : function(title, msg, fn, scope){
15590             this.show({
15591                 title : title,
15592                 msg : msg,
15593                 buttons: this.OK,
15594                 fn: fn,
15595                 scope : scope,
15596                 modal : true
15597             });
15598             return this;
15599         },
15600
15601         /**
15602          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15603          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15604          * You are responsible for closing the message box when the process is complete.
15605          * @param {String} msg The message box body text
15606          * @param {String} title (optional) The title bar text
15607          * @return {Roo.MessageBox} This message box
15608          */
15609         wait : function(msg, title){
15610             this.show({
15611                 title : title,
15612                 msg : msg,
15613                 buttons: false,
15614                 closable:false,
15615                 progress:true,
15616                 modal:true,
15617                 width:300,
15618                 wait:true
15619             });
15620             waitTimer = Roo.TaskMgr.start({
15621                 run: function(i){
15622                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15623                 },
15624                 interval: 1000
15625             });
15626             return this;
15627         },
15628
15629         /**
15630          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15631          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15632          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15633          * @param {String} title The title bar text
15634          * @param {String} msg The message box body text
15635          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15636          * @param {Object} scope (optional) The scope of the callback function
15637          * @return {Roo.MessageBox} This message box
15638          */
15639         confirm : function(title, msg, fn, scope){
15640             this.show({
15641                 title : title,
15642                 msg : msg,
15643                 buttons: this.YESNO,
15644                 fn: fn,
15645                 scope : scope,
15646                 modal : true
15647             });
15648             return this;
15649         },
15650
15651         /**
15652          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15653          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15654          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15655          * (could also be the top-right close button) and the text that was entered will be passed as the two
15656          * parameters to the callback.
15657          * @param {String} title The title bar text
15658          * @param {String} msg The message box body text
15659          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15660          * @param {Object} scope (optional) The scope of the callback function
15661          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15662          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15663          * @return {Roo.MessageBox} This message box
15664          */
15665         prompt : function(title, msg, fn, scope, multiline){
15666             this.show({
15667                 title : title,
15668                 msg : msg,
15669                 buttons: this.OKCANCEL,
15670                 fn: fn,
15671                 minWidth:250,
15672                 scope : scope,
15673                 prompt:true,
15674                 multiline: multiline,
15675                 modal : true
15676             });
15677             return this;
15678         },
15679
15680         /**
15681          * Button config that displays a single OK button
15682          * @type Object
15683          */
15684         OK : {ok:true},
15685         /**
15686          * Button config that displays Yes and No buttons
15687          * @type Object
15688          */
15689         YESNO : {yes:true, no:true},
15690         /**
15691          * Button config that displays OK and Cancel buttons
15692          * @type Object
15693          */
15694         OKCANCEL : {ok:true, cancel:true},
15695         /**
15696          * Button config that displays Yes, No and Cancel buttons
15697          * @type Object
15698          */
15699         YESNOCANCEL : {yes:true, no:true, cancel:true},
15700
15701         /**
15702          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15703          * @type Number
15704          */
15705         defaultTextHeight : 75,
15706         /**
15707          * The maximum width in pixels of the message box (defaults to 600)
15708          * @type Number
15709          */
15710         maxWidth : 600,
15711         /**
15712          * The minimum width in pixels of the message box (defaults to 100)
15713          * @type Number
15714          */
15715         minWidth : 100,
15716         /**
15717          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15718          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15719          * @type Number
15720          */
15721         minProgressWidth : 250,
15722         /**
15723          * An object containing the default button text strings that can be overriden for localized language support.
15724          * Supported properties are: ok, cancel, yes and no.
15725          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15726          * @type Object
15727          */
15728         buttonText : {
15729             ok : "OK",
15730             cancel : "Cancel",
15731             yes : "Yes",
15732             no : "No"
15733         }
15734     };
15735 }();
15736
15737 /**
15738  * Shorthand for {@link Roo.MessageBox}
15739  */
15740 Roo.Msg = Roo.MessageBox;/*
15741  * Based on:
15742  * Ext JS Library 1.1.1
15743  * Copyright(c) 2006-2007, Ext JS, LLC.
15744  *
15745  * Originally Released Under LGPL - original licence link has changed is not relivant.
15746  *
15747  * Fork - LGPL
15748  * <script type="text/javascript">
15749  */
15750 /**
15751  * @class Roo.QuickTips
15752  * Provides attractive and customizable tooltips for any element.
15753  * @singleton
15754  */
15755 Roo.QuickTips = function(){
15756     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15757     var ce, bd, xy, dd;
15758     var visible = false, disabled = true, inited = false;
15759     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15760     
15761     var onOver = function(e){
15762         if(disabled){
15763             return;
15764         }
15765         var t = e.getTarget();
15766         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15767             return;
15768         }
15769         if(ce && t == ce.el){
15770             clearTimeout(hideProc);
15771             return;
15772         }
15773         if(t && tagEls[t.id]){
15774             tagEls[t.id].el = t;
15775             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15776             return;
15777         }
15778         var ttp, et = Roo.fly(t);
15779         var ns = cfg.namespace;
15780         if(tm.interceptTitles && t.title){
15781             ttp = t.title;
15782             t.qtip = ttp;
15783             t.removeAttribute("title");
15784             e.preventDefault();
15785         }else{
15786             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15787         }
15788         if(ttp){
15789             showProc = show.defer(tm.showDelay, tm, [{
15790                 el: t, 
15791                 text: ttp, 
15792                 width: et.getAttributeNS(ns, cfg.width),
15793                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15794                 title: et.getAttributeNS(ns, cfg.title),
15795                     cls: et.getAttributeNS(ns, cfg.cls)
15796             }]);
15797         }
15798     };
15799     
15800     var onOut = function(e){
15801         clearTimeout(showProc);
15802         var t = e.getTarget();
15803         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15804             hideProc = setTimeout(hide, tm.hideDelay);
15805         }
15806     };
15807     
15808     var onMove = function(e){
15809         if(disabled){
15810             return;
15811         }
15812         xy = e.getXY();
15813         xy[1] += 18;
15814         if(tm.trackMouse && ce){
15815             el.setXY(xy);
15816         }
15817     };
15818     
15819     var onDown = function(e){
15820         clearTimeout(showProc);
15821         clearTimeout(hideProc);
15822         if(!e.within(el)){
15823             if(tm.hideOnClick){
15824                 hide();
15825                 tm.disable();
15826                 tm.enable.defer(100, tm);
15827             }
15828         }
15829     };
15830     
15831     var getPad = function(){
15832         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15833     };
15834
15835     var show = function(o){
15836         if(disabled){
15837             return;
15838         }
15839         clearTimeout(dismissProc);
15840         ce = o;
15841         if(removeCls){ // in case manually hidden
15842             el.removeClass(removeCls);
15843             removeCls = null;
15844         }
15845         if(ce.cls){
15846             el.addClass(ce.cls);
15847             removeCls = ce.cls;
15848         }
15849         if(ce.title){
15850             tipTitle.update(ce.title);
15851             tipTitle.show();
15852         }else{
15853             tipTitle.update('');
15854             tipTitle.hide();
15855         }
15856         el.dom.style.width  = tm.maxWidth+'px';
15857         //tipBody.dom.style.width = '';
15858         tipBodyText.update(o.text);
15859         var p = getPad(), w = ce.width;
15860         if(!w){
15861             var td = tipBodyText.dom;
15862             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15863             if(aw > tm.maxWidth){
15864                 w = tm.maxWidth;
15865             }else if(aw < tm.minWidth){
15866                 w = tm.minWidth;
15867             }else{
15868                 w = aw;
15869             }
15870         }
15871         //tipBody.setWidth(w);
15872         el.setWidth(parseInt(w, 10) + p);
15873         if(ce.autoHide === false){
15874             close.setDisplayed(true);
15875             if(dd){
15876                 dd.unlock();
15877             }
15878         }else{
15879             close.setDisplayed(false);
15880             if(dd){
15881                 dd.lock();
15882             }
15883         }
15884         if(xy){
15885             el.avoidY = xy[1]-18;
15886             el.setXY(xy);
15887         }
15888         if(tm.animate){
15889             el.setOpacity(.1);
15890             el.setStyle("visibility", "visible");
15891             el.fadeIn({callback: afterShow});
15892         }else{
15893             afterShow();
15894         }
15895     };
15896     
15897     var afterShow = function(){
15898         if(ce){
15899             el.show();
15900             esc.enable();
15901             if(tm.autoDismiss && ce.autoHide !== false){
15902                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15903             }
15904         }
15905     };
15906     
15907     var hide = function(noanim){
15908         clearTimeout(dismissProc);
15909         clearTimeout(hideProc);
15910         ce = null;
15911         if(el.isVisible()){
15912             esc.disable();
15913             if(noanim !== true && tm.animate){
15914                 el.fadeOut({callback: afterHide});
15915             }else{
15916                 afterHide();
15917             } 
15918         }
15919     };
15920     
15921     var afterHide = function(){
15922         el.hide();
15923         if(removeCls){
15924             el.removeClass(removeCls);
15925             removeCls = null;
15926         }
15927     };
15928     
15929     return {
15930         /**
15931         * @cfg {Number} minWidth
15932         * The minimum width of the quick tip (defaults to 40)
15933         */
15934        minWidth : 40,
15935         /**
15936         * @cfg {Number} maxWidth
15937         * The maximum width of the quick tip (defaults to 300)
15938         */
15939        maxWidth : 300,
15940         /**
15941         * @cfg {Boolean} interceptTitles
15942         * True to automatically use the element's DOM title value if available (defaults to false)
15943         */
15944        interceptTitles : false,
15945         /**
15946         * @cfg {Boolean} trackMouse
15947         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15948         */
15949        trackMouse : false,
15950         /**
15951         * @cfg {Boolean} hideOnClick
15952         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15953         */
15954        hideOnClick : true,
15955         /**
15956         * @cfg {Number} showDelay
15957         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15958         */
15959        showDelay : 500,
15960         /**
15961         * @cfg {Number} hideDelay
15962         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15963         */
15964        hideDelay : 200,
15965         /**
15966         * @cfg {Boolean} autoHide
15967         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
15968         * Used in conjunction with hideDelay.
15969         */
15970        autoHide : true,
15971         /**
15972         * @cfg {Boolean}
15973         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
15974         * (defaults to true).  Used in conjunction with autoDismissDelay.
15975         */
15976        autoDismiss : true,
15977         /**
15978         * @cfg {Number}
15979         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
15980         */
15981        autoDismissDelay : 5000,
15982        /**
15983         * @cfg {Boolean} animate
15984         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
15985         */
15986        animate : false,
15987
15988        /**
15989         * @cfg {String} title
15990         * Title text to display (defaults to '').  This can be any valid HTML markup.
15991         */
15992         title: '',
15993        /**
15994         * @cfg {String} text
15995         * Body text to display (defaults to '').  This can be any valid HTML markup.
15996         */
15997         text : '',
15998        /**
15999         * @cfg {String} cls
16000         * A CSS class to apply to the base quick tip element (defaults to '').
16001         */
16002         cls : '',
16003        /**
16004         * @cfg {Number} width
16005         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16006         * minWidth or maxWidth.
16007         */
16008         width : null,
16009
16010     /**
16011      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16012      * or display QuickTips in a page.
16013      */
16014        init : function(){
16015           tm = Roo.QuickTips;
16016           cfg = tm.tagConfig;
16017           if(!inited){
16018               if(!Roo.isReady){ // allow calling of init() before onReady
16019                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16020                   return;
16021               }
16022               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16023               el.fxDefaults = {stopFx: true};
16024               // maximum custom styling
16025               //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>');
16026               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>');              
16027               tipTitle = el.child('h3');
16028               tipTitle.enableDisplayMode("block");
16029               tipBody = el.child('div.x-tip-bd');
16030               tipBodyText = el.child('div.x-tip-bd-inner');
16031               //bdLeft = el.child('div.x-tip-bd-left');
16032               //bdRight = el.child('div.x-tip-bd-right');
16033               close = el.child('div.x-tip-close');
16034               close.enableDisplayMode("block");
16035               close.on("click", hide);
16036               var d = Roo.get(document);
16037               d.on("mousedown", onDown);
16038               d.on("mouseover", onOver);
16039               d.on("mouseout", onOut);
16040               d.on("mousemove", onMove);
16041               esc = d.addKeyListener(27, hide);
16042               esc.disable();
16043               if(Roo.dd.DD){
16044                   dd = el.initDD("default", null, {
16045                       onDrag : function(){
16046                           el.sync();  
16047                       }
16048                   });
16049                   dd.setHandleElId(tipTitle.id);
16050                   dd.lock();
16051               }
16052               inited = true;
16053           }
16054           this.enable(); 
16055        },
16056
16057     /**
16058      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16059      * are supported:
16060      * <pre>
16061 Property    Type                   Description
16062 ----------  ---------------------  ------------------------------------------------------------------------
16063 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16064      * </ul>
16065      * @param {Object} config The config object
16066      */
16067        register : function(config){
16068            var cs = config instanceof Array ? config : arguments;
16069            for(var i = 0, len = cs.length; i < len; i++) {
16070                var c = cs[i];
16071                var target = c.target;
16072                if(target){
16073                    if(target instanceof Array){
16074                        for(var j = 0, jlen = target.length; j < jlen; j++){
16075                            tagEls[target[j]] = c;
16076                        }
16077                    }else{
16078                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16079                    }
16080                }
16081            }
16082        },
16083
16084     /**
16085      * Removes this quick tip from its element and destroys it.
16086      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16087      */
16088        unregister : function(el){
16089            delete tagEls[Roo.id(el)];
16090        },
16091
16092     /**
16093      * Enable this quick tip.
16094      */
16095        enable : function(){
16096            if(inited && disabled){
16097                locks.pop();
16098                if(locks.length < 1){
16099                    disabled = false;
16100                }
16101            }
16102        },
16103
16104     /**
16105      * Disable this quick tip.
16106      */
16107        disable : function(){
16108           disabled = true;
16109           clearTimeout(showProc);
16110           clearTimeout(hideProc);
16111           clearTimeout(dismissProc);
16112           if(ce){
16113               hide(true);
16114           }
16115           locks.push(1);
16116        },
16117
16118     /**
16119      * Returns true if the quick tip is enabled, else false.
16120      */
16121        isEnabled : function(){
16122             return !disabled;
16123        },
16124
16125         // private
16126        tagConfig : {
16127            namespace : "ext",
16128            attribute : "qtip",
16129            width : "width",
16130            target : "target",
16131            title : "qtitle",
16132            hide : "hide",
16133            cls : "qclass"
16134        }
16135    };
16136 }();
16137
16138 // backwards compat
16139 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16140  * Based on:
16141  * Ext JS Library 1.1.1
16142  * Copyright(c) 2006-2007, Ext JS, LLC.
16143  *
16144  * Originally Released Under LGPL - original licence link has changed is not relivant.
16145  *
16146  * Fork - LGPL
16147  * <script type="text/javascript">
16148  */
16149  
16150
16151 /**
16152  * @class Roo.tree.TreePanel
16153  * @extends Roo.data.Tree
16154
16155  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16156  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16157  * @cfg {Boolean} enableDD true to enable drag and drop
16158  * @cfg {Boolean} enableDrag true to enable just drag
16159  * @cfg {Boolean} enableDrop true to enable just drop
16160  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16161  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16162  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16163  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16164  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16165  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16166  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16167  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16168  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16169  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16170  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16171  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16172  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16173  * @cfg {Function} renderer 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>
16174  * @cfg {Function} rendererTip 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>
16175  * 
16176  * @constructor
16177  * @param {String/HTMLElement/Element} el The container element
16178  * @param {Object} config
16179  */
16180 Roo.tree.TreePanel = function(el, config){
16181     var root = false;
16182     var loader = false;
16183     if (config.root) {
16184         root = config.root;
16185         delete config.root;
16186     }
16187     if (config.loader) {
16188         loader = config.loader;
16189         delete config.loader;
16190     }
16191     
16192     Roo.apply(this, config);
16193     Roo.tree.TreePanel.superclass.constructor.call(this);
16194     this.el = Roo.get(el);
16195     this.el.addClass('x-tree');
16196     //console.log(root);
16197     if (root) {
16198         this.setRootNode( Roo.factory(root, Roo.tree));
16199     }
16200     if (loader) {
16201         this.loader = Roo.factory(loader, Roo.tree);
16202     }
16203    /**
16204     * Read-only. The id of the container element becomes this TreePanel's id.
16205     */
16206    this.id = this.el.id;
16207    this.addEvents({
16208         /**
16209         * @event beforeload
16210         * Fires before a node is loaded, return false to cancel
16211         * @param {Node} node The node being loaded
16212         */
16213         "beforeload" : true,
16214         /**
16215         * @event load
16216         * Fires when a node is loaded
16217         * @param {Node} node The node that was loaded
16218         */
16219         "load" : true,
16220         /**
16221         * @event textchange
16222         * Fires when the text for a node is changed
16223         * @param {Node} node The node
16224         * @param {String} text The new text
16225         * @param {String} oldText The old text
16226         */
16227         "textchange" : true,
16228         /**
16229         * @event beforeexpand
16230         * Fires before a node is expanded, return false to cancel.
16231         * @param {Node} node The node
16232         * @param {Boolean} deep
16233         * @param {Boolean} anim
16234         */
16235         "beforeexpand" : true,
16236         /**
16237         * @event beforecollapse
16238         * Fires before a node is collapsed, return false to cancel.
16239         * @param {Node} node The node
16240         * @param {Boolean} deep
16241         * @param {Boolean} anim
16242         */
16243         "beforecollapse" : true,
16244         /**
16245         * @event expand
16246         * Fires when a node is expanded
16247         * @param {Node} node The node
16248         */
16249         "expand" : true,
16250         /**
16251         * @event disabledchange
16252         * Fires when the disabled status of a node changes
16253         * @param {Node} node The node
16254         * @param {Boolean} disabled
16255         */
16256         "disabledchange" : true,
16257         /**
16258         * @event collapse
16259         * Fires when a node is collapsed
16260         * @param {Node} node The node
16261         */
16262         "collapse" : true,
16263         /**
16264         * @event beforeclick
16265         * Fires before click processing on a node. Return false to cancel the default action.
16266         * @param {Node} node The node
16267         * @param {Roo.EventObject} e The event object
16268         */
16269         "beforeclick":true,
16270         /**
16271         * @event checkchange
16272         * Fires when a node with a checkbox's checked property changes
16273         * @param {Node} this This node
16274         * @param {Boolean} checked
16275         */
16276         "checkchange":true,
16277         /**
16278         * @event click
16279         * Fires when a node is clicked
16280         * @param {Node} node The node
16281         * @param {Roo.EventObject} e The event object
16282         */
16283         "click":true,
16284         /**
16285         * @event dblclick
16286         * Fires when a node is double clicked
16287         * @param {Node} node The node
16288         * @param {Roo.EventObject} e The event object
16289         */
16290         "dblclick":true,
16291         /**
16292         * @event contextmenu
16293         * Fires when a node is right clicked
16294         * @param {Node} node The node
16295         * @param {Roo.EventObject} e The event object
16296         */
16297         "contextmenu":true,
16298         /**
16299         * @event beforechildrenrendered
16300         * Fires right before the child nodes for a node are rendered
16301         * @param {Node} node The node
16302         */
16303         "beforechildrenrendered":true,
16304        /**
16305              * @event startdrag
16306              * Fires when a node starts being dragged
16307              * @param {Roo.tree.TreePanel} this
16308              * @param {Roo.tree.TreeNode} node
16309              * @param {event} e The raw browser event
16310              */ 
16311             "startdrag" : true,
16312             /**
16313              * @event enddrag
16314              * Fires when a drag operation is complete
16315              * @param {Roo.tree.TreePanel} this
16316              * @param {Roo.tree.TreeNode} node
16317              * @param {event} e The raw browser event
16318              */
16319             "enddrag" : true,
16320             /**
16321              * @event dragdrop
16322              * Fires when a dragged node is dropped on a valid DD target
16323              * @param {Roo.tree.TreePanel} this
16324              * @param {Roo.tree.TreeNode} node
16325              * @param {DD} dd The dd it was dropped on
16326              * @param {event} e The raw browser event
16327              */
16328             "dragdrop" : true,
16329             /**
16330              * @event beforenodedrop
16331              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16332              * passed to handlers has the following properties:<br />
16333              * <ul style="padding:5px;padding-left:16px;">
16334              * <li>tree - The TreePanel</li>
16335              * <li>target - The node being targeted for the drop</li>
16336              * <li>data - The drag data from the drag source</li>
16337              * <li>point - The point of the drop - append, above or below</li>
16338              * <li>source - The drag source</li>
16339              * <li>rawEvent - Raw mouse event</li>
16340              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16341              * to be inserted by setting them on this object.</li>
16342              * <li>cancel - Set this to true to cancel the drop.</li>
16343              * </ul>
16344              * @param {Object} dropEvent
16345              */
16346             "beforenodedrop" : true,
16347             /**
16348              * @event nodedrop
16349              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16350              * passed to handlers has the following properties:<br />
16351              * <ul style="padding:5px;padding-left:16px;">
16352              * <li>tree - The TreePanel</li>
16353              * <li>target - The node being targeted for the drop</li>
16354              * <li>data - The drag data from the drag source</li>
16355              * <li>point - The point of the drop - append, above or below</li>
16356              * <li>source - The drag source</li>
16357              * <li>rawEvent - Raw mouse event</li>
16358              * <li>dropNode - Dropped node(s).</li>
16359              * </ul>
16360              * @param {Object} dropEvent
16361              */
16362             "nodedrop" : true,
16363              /**
16364              * @event nodedragover
16365              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16366              * passed to handlers has the following properties:<br />
16367              * <ul style="padding:5px;padding-left:16px;">
16368              * <li>tree - The TreePanel</li>
16369              * <li>target - The node being targeted for the drop</li>
16370              * <li>data - The drag data from the drag source</li>
16371              * <li>point - The point of the drop - append, above or below</li>
16372              * <li>source - The drag source</li>
16373              * <li>rawEvent - Raw mouse event</li>
16374              * <li>dropNode - Drop node(s) provided by the source.</li>
16375              * <li>cancel - Set this to true to signal drop not allowed.</li>
16376              * </ul>
16377              * @param {Object} dragOverEvent
16378              */
16379             "nodedragover" : true
16380         
16381    });
16382    if(this.singleExpand){
16383        this.on("beforeexpand", this.restrictExpand, this);
16384    }
16385 };
16386 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16387     rootVisible : true,
16388     animate: Roo.enableFx,
16389     lines : true,
16390     enableDD : false,
16391     hlDrop : Roo.enableFx,
16392   
16393     renderer: false,
16394     
16395     rendererTip: false,
16396     // private
16397     restrictExpand : function(node){
16398         var p = node.parentNode;
16399         if(p){
16400             if(p.expandedChild && p.expandedChild.parentNode == p){
16401                 p.expandedChild.collapse();
16402             }
16403             p.expandedChild = node;
16404         }
16405     },
16406
16407     // private override
16408     setRootNode : function(node){
16409         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16410         if(!this.rootVisible){
16411             node.ui = new Roo.tree.RootTreeNodeUI(node);
16412         }
16413         return node;
16414     },
16415
16416     /**
16417      * Returns the container element for this TreePanel
16418      */
16419     getEl : function(){
16420         return this.el;
16421     },
16422
16423     /**
16424      * Returns the default TreeLoader for this TreePanel
16425      */
16426     getLoader : function(){
16427         return this.loader;
16428     },
16429
16430     /**
16431      * Expand all nodes
16432      */
16433     expandAll : function(){
16434         this.root.expand(true);
16435     },
16436
16437     /**
16438      * Collapse all nodes
16439      */
16440     collapseAll : function(){
16441         this.root.collapse(true);
16442     },
16443
16444     /**
16445      * Returns the selection model used by this TreePanel
16446      */
16447     getSelectionModel : function(){
16448         if(!this.selModel){
16449             this.selModel = new Roo.tree.DefaultSelectionModel();
16450         }
16451         return this.selModel;
16452     },
16453
16454     /**
16455      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16456      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16457      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16458      * @return {Array}
16459      */
16460     getChecked : function(a, startNode){
16461         startNode = startNode || this.root;
16462         var r = [];
16463         var f = function(){
16464             if(this.attributes.checked){
16465                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16466             }
16467         }
16468         startNode.cascade(f);
16469         return r;
16470     },
16471
16472     /**
16473      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16474      * @param {String} path
16475      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16476      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16477      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16478      */
16479     expandPath : function(path, attr, callback){
16480         attr = attr || "id";
16481         var keys = path.split(this.pathSeparator);
16482         var curNode = this.root;
16483         if(curNode.attributes[attr] != keys[1]){ // invalid root
16484             if(callback){
16485                 callback(false, null);
16486             }
16487             return;
16488         }
16489         var index = 1;
16490         var f = function(){
16491             if(++index == keys.length){
16492                 if(callback){
16493                     callback(true, curNode);
16494                 }
16495                 return;
16496             }
16497             var c = curNode.findChild(attr, keys[index]);
16498             if(!c){
16499                 if(callback){
16500                     callback(false, curNode);
16501                 }
16502                 return;
16503             }
16504             curNode = c;
16505             c.expand(false, false, f);
16506         };
16507         curNode.expand(false, false, f);
16508     },
16509
16510     /**
16511      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16512      * @param {String} path
16513      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16514      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16515      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16516      */
16517     selectPath : function(path, attr, callback){
16518         attr = attr || "id";
16519         var keys = path.split(this.pathSeparator);
16520         var v = keys.pop();
16521         if(keys.length > 0){
16522             var f = function(success, node){
16523                 if(success && node){
16524                     var n = node.findChild(attr, v);
16525                     if(n){
16526                         n.select();
16527                         if(callback){
16528                             callback(true, n);
16529                         }
16530                     }else if(callback){
16531                         callback(false, n);
16532                     }
16533                 }else{
16534                     if(callback){
16535                         callback(false, n);
16536                     }
16537                 }
16538             };
16539             this.expandPath(keys.join(this.pathSeparator), attr, f);
16540         }else{
16541             this.root.select();
16542             if(callback){
16543                 callback(true, this.root);
16544             }
16545         }
16546     },
16547
16548     getTreeEl : function(){
16549         return this.el;
16550     },
16551
16552     /**
16553      * Trigger rendering of this TreePanel
16554      */
16555     render : function(){
16556         if (this.innerCt) {
16557             return this; // stop it rendering more than once!!
16558         }
16559         
16560         this.innerCt = this.el.createChild({tag:"ul",
16561                cls:"x-tree-root-ct " +
16562                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16563
16564         if(this.containerScroll){
16565             Roo.dd.ScrollManager.register(this.el);
16566         }
16567         if((this.enableDD || this.enableDrop) && !this.dropZone){
16568            /**
16569             * The dropZone used by this tree if drop is enabled
16570             * @type Roo.tree.TreeDropZone
16571             */
16572              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16573                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16574            });
16575         }
16576         if((this.enableDD || this.enableDrag) && !this.dragZone){
16577            /**
16578             * The dragZone used by this tree if drag is enabled
16579             * @type Roo.tree.TreeDragZone
16580             */
16581             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16582                ddGroup: this.ddGroup || "TreeDD",
16583                scroll: this.ddScroll
16584            });
16585         }
16586         this.getSelectionModel().init(this);
16587         if (!this.root) {
16588             console.log("ROOT not set in tree");
16589             return;
16590         }
16591         this.root.render();
16592         if(!this.rootVisible){
16593             this.root.renderChildren();
16594         }
16595         return this;
16596     }
16597 });/*
16598  * Based on:
16599  * Ext JS Library 1.1.1
16600  * Copyright(c) 2006-2007, Ext JS, LLC.
16601  *
16602  * Originally Released Under LGPL - original licence link has changed is not relivant.
16603  *
16604  * Fork - LGPL
16605  * <script type="text/javascript">
16606  */
16607  
16608
16609 /**
16610  * @class Roo.tree.DefaultSelectionModel
16611  * @extends Roo.util.Observable
16612  * The default single selection for a TreePanel.
16613  */
16614 Roo.tree.DefaultSelectionModel = function(){
16615    this.selNode = null;
16616    
16617    this.addEvents({
16618        /**
16619         * @event selectionchange
16620         * Fires when the selected node changes
16621         * @param {DefaultSelectionModel} this
16622         * @param {TreeNode} node the new selection
16623         */
16624        "selectionchange" : true,
16625
16626        /**
16627         * @event beforeselect
16628         * Fires before the selected node changes, return false to cancel the change
16629         * @param {DefaultSelectionModel} this
16630         * @param {TreeNode} node the new selection
16631         * @param {TreeNode} node the old selection
16632         */
16633        "beforeselect" : true
16634    });
16635 };
16636
16637 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16638     init : function(tree){
16639         this.tree = tree;
16640         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16641         tree.on("click", this.onNodeClick, this);
16642     },
16643     
16644     onNodeClick : function(node, e){
16645         if (e.ctrlKey && this.selNode == node)  {
16646             this.unselect(node);
16647             return;
16648         }
16649         this.select(node);
16650     },
16651     
16652     /**
16653      * Select a node.
16654      * @param {TreeNode} node The node to select
16655      * @return {TreeNode} The selected node
16656      */
16657     select : function(node){
16658         var last = this.selNode;
16659         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16660             if(last){
16661                 last.ui.onSelectedChange(false);
16662             }
16663             this.selNode = node;
16664             node.ui.onSelectedChange(true);
16665             this.fireEvent("selectionchange", this, node, last);
16666         }
16667         return node;
16668     },
16669     
16670     /**
16671      * Deselect a node.
16672      * @param {TreeNode} node The node to unselect
16673      */
16674     unselect : function(node){
16675         if(this.selNode == node){
16676             this.clearSelections();
16677         }    
16678     },
16679     
16680     /**
16681      * Clear all selections
16682      */
16683     clearSelections : function(){
16684         var n = this.selNode;
16685         if(n){
16686             n.ui.onSelectedChange(false);
16687             this.selNode = null;
16688             this.fireEvent("selectionchange", this, null);
16689         }
16690         return n;
16691     },
16692     
16693     /**
16694      * Get the selected node
16695      * @return {TreeNode} The selected node
16696      */
16697     getSelectedNode : function(){
16698         return this.selNode;    
16699     },
16700     
16701     /**
16702      * Returns true if the node is selected
16703      * @param {TreeNode} node The node to check
16704      * @return {Boolean}
16705      */
16706     isSelected : function(node){
16707         return this.selNode == node;  
16708     },
16709
16710     /**
16711      * Selects the node above the selected node in the tree, intelligently walking the nodes
16712      * @return TreeNode The new selection
16713      */
16714     selectPrevious : function(){
16715         var s = this.selNode || this.lastSelNode;
16716         if(!s){
16717             return null;
16718         }
16719         var ps = s.previousSibling;
16720         if(ps){
16721             if(!ps.isExpanded() || ps.childNodes.length < 1){
16722                 return this.select(ps);
16723             } else{
16724                 var lc = ps.lastChild;
16725                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16726                     lc = lc.lastChild;
16727                 }
16728                 return this.select(lc);
16729             }
16730         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16731             return this.select(s.parentNode);
16732         }
16733         return null;
16734     },
16735
16736     /**
16737      * Selects the node above the selected node in the tree, intelligently walking the nodes
16738      * @return TreeNode The new selection
16739      */
16740     selectNext : function(){
16741         var s = this.selNode || this.lastSelNode;
16742         if(!s){
16743             return null;
16744         }
16745         if(s.firstChild && s.isExpanded()){
16746              return this.select(s.firstChild);
16747          }else if(s.nextSibling){
16748              return this.select(s.nextSibling);
16749          }else if(s.parentNode){
16750             var newS = null;
16751             s.parentNode.bubble(function(){
16752                 if(this.nextSibling){
16753                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16754                     return false;
16755                 }
16756             });
16757             return newS;
16758          }
16759         return null;
16760     },
16761
16762     onKeyDown : function(e){
16763         var s = this.selNode || this.lastSelNode;
16764         // undesirable, but required
16765         var sm = this;
16766         if(!s){
16767             return;
16768         }
16769         var k = e.getKey();
16770         switch(k){
16771              case e.DOWN:
16772                  e.stopEvent();
16773                  this.selectNext();
16774              break;
16775              case e.UP:
16776                  e.stopEvent();
16777                  this.selectPrevious();
16778              break;
16779              case e.RIGHT:
16780                  e.preventDefault();
16781                  if(s.hasChildNodes()){
16782                      if(!s.isExpanded()){
16783                          s.expand();
16784                      }else if(s.firstChild){
16785                          this.select(s.firstChild, e);
16786                      }
16787                  }
16788              break;
16789              case e.LEFT:
16790                  e.preventDefault();
16791                  if(s.hasChildNodes() && s.isExpanded()){
16792                      s.collapse();
16793                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16794                      this.select(s.parentNode, e);
16795                  }
16796              break;
16797         };
16798     }
16799 });
16800
16801 /**
16802  * @class Roo.tree.MultiSelectionModel
16803  * @extends Roo.util.Observable
16804  * Multi selection for a TreePanel.
16805  */
16806 Roo.tree.MultiSelectionModel = function(){
16807    this.selNodes = [];
16808    this.selMap = {};
16809    this.addEvents({
16810        /**
16811         * @event selectionchange
16812         * Fires when the selected nodes change
16813         * @param {MultiSelectionModel} this
16814         * @param {Array} nodes Array of the selected nodes
16815         */
16816        "selectionchange" : true
16817    });
16818 };
16819
16820 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16821     init : function(tree){
16822         this.tree = tree;
16823         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16824         tree.on("click", this.onNodeClick, this);
16825     },
16826     
16827     onNodeClick : function(node, e){
16828         this.select(node, e, e.ctrlKey);
16829     },
16830     
16831     /**
16832      * Select a node.
16833      * @param {TreeNode} node The node to select
16834      * @param {EventObject} e (optional) An event associated with the selection
16835      * @param {Boolean} keepExisting True to retain existing selections
16836      * @return {TreeNode} The selected node
16837      */
16838     select : function(node, e, keepExisting){
16839         if(keepExisting !== true){
16840             this.clearSelections(true);
16841         }
16842         if(this.isSelected(node)){
16843             this.lastSelNode = node;
16844             return node;
16845         }
16846         this.selNodes.push(node);
16847         this.selMap[node.id] = node;
16848         this.lastSelNode = node;
16849         node.ui.onSelectedChange(true);
16850         this.fireEvent("selectionchange", this, this.selNodes);
16851         return node;
16852     },
16853     
16854     /**
16855      * Deselect a node.
16856      * @param {TreeNode} node The node to unselect
16857      */
16858     unselect : function(node){
16859         if(this.selMap[node.id]){
16860             node.ui.onSelectedChange(false);
16861             var sn = this.selNodes;
16862             var index = -1;
16863             if(sn.indexOf){
16864                 index = sn.indexOf(node);
16865             }else{
16866                 for(var i = 0, len = sn.length; i < len; i++){
16867                     if(sn[i] == node){
16868                         index = i;
16869                         break;
16870                     }
16871                 }
16872             }
16873             if(index != -1){
16874                 this.selNodes.splice(index, 1);
16875             }
16876             delete this.selMap[node.id];
16877             this.fireEvent("selectionchange", this, this.selNodes);
16878         }
16879     },
16880     
16881     /**
16882      * Clear all selections
16883      */
16884     clearSelections : function(suppressEvent){
16885         var sn = this.selNodes;
16886         if(sn.length > 0){
16887             for(var i = 0, len = sn.length; i < len; i++){
16888                 sn[i].ui.onSelectedChange(false);
16889             }
16890             this.selNodes = [];
16891             this.selMap = {};
16892             if(suppressEvent !== true){
16893                 this.fireEvent("selectionchange", this, this.selNodes);
16894             }
16895         }
16896     },
16897     
16898     /**
16899      * Returns true if the node is selected
16900      * @param {TreeNode} node The node to check
16901      * @return {Boolean}
16902      */
16903     isSelected : function(node){
16904         return this.selMap[node.id] ? true : false;  
16905     },
16906     
16907     /**
16908      * Returns an array of the selected nodes
16909      * @return {Array}
16910      */
16911     getSelectedNodes : function(){
16912         return this.selNodes;    
16913     },
16914
16915     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16916
16917     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16918
16919     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16920 });/*
16921  * Based on:
16922  * Ext JS Library 1.1.1
16923  * Copyright(c) 2006-2007, Ext JS, LLC.
16924  *
16925  * Originally Released Under LGPL - original licence link has changed is not relivant.
16926  *
16927  * Fork - LGPL
16928  * <script type="text/javascript">
16929  */
16930  
16931 /**
16932  * @class Roo.tree.TreeNode
16933  * @extends Roo.data.Node
16934  * @cfg {String} text The text for this node
16935  * @cfg {Boolean} expanded true to start the node expanded
16936  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16937  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16938  * @cfg {Boolean} disabled true to start the node disabled
16939  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16940  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16941  * @cfg {String} cls A css class to be added to the node
16942  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16943  * @cfg {String} href URL of the link used for the node (defaults to #)
16944  * @cfg {String} hrefTarget target frame for the link
16945  * @cfg {String} qtip An Ext QuickTip for the node
16946  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16947  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16948  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16949  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16950  * (defaults to undefined with no checkbox rendered)
16951  * @constructor
16952  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16953  */
16954 Roo.tree.TreeNode = function(attributes){
16955     attributes = attributes || {};
16956     if(typeof attributes == "string"){
16957         attributes = {text: attributes};
16958     }
16959     this.childrenRendered = false;
16960     this.rendered = false;
16961     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16962     this.expanded = attributes.expanded === true;
16963     this.isTarget = attributes.isTarget !== false;
16964     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16965     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
16966
16967     /**
16968      * Read-only. The text for this node. To change it use setText().
16969      * @type String
16970      */
16971     this.text = attributes.text;
16972     /**
16973      * True if this node is disabled.
16974      * @type Boolean
16975      */
16976     this.disabled = attributes.disabled === true;
16977
16978     this.addEvents({
16979         /**
16980         * @event textchange
16981         * Fires when the text for this node is changed
16982         * @param {Node} this This node
16983         * @param {String} text The new text
16984         * @param {String} oldText The old text
16985         */
16986         "textchange" : true,
16987         /**
16988         * @event beforeexpand
16989         * Fires before this node is expanded, return false to cancel.
16990         * @param {Node} this This node
16991         * @param {Boolean} deep
16992         * @param {Boolean} anim
16993         */
16994         "beforeexpand" : true,
16995         /**
16996         * @event beforecollapse
16997         * Fires before this node is collapsed, return false to cancel.
16998         * @param {Node} this This node
16999         * @param {Boolean} deep
17000         * @param {Boolean} anim
17001         */
17002         "beforecollapse" : true,
17003         /**
17004         * @event expand
17005         * Fires when this node is expanded
17006         * @param {Node} this This node
17007         */
17008         "expand" : true,
17009         /**
17010         * @event disabledchange
17011         * Fires when the disabled status of this node changes
17012         * @param {Node} this This node
17013         * @param {Boolean} disabled
17014         */
17015         "disabledchange" : true,
17016         /**
17017         * @event collapse
17018         * Fires when this node is collapsed
17019         * @param {Node} this This node
17020         */
17021         "collapse" : true,
17022         /**
17023         * @event beforeclick
17024         * Fires before click processing. Return false to cancel the default action.
17025         * @param {Node} this This node
17026         * @param {Roo.EventObject} e The event object
17027         */
17028         "beforeclick":true,
17029         /**
17030         * @event checkchange
17031         * Fires when a node with a checkbox's checked property changes
17032         * @param {Node} this This node
17033         * @param {Boolean} checked
17034         */
17035         "checkchange":true,
17036         /**
17037         * @event click
17038         * Fires when this node is clicked
17039         * @param {Node} this This node
17040         * @param {Roo.EventObject} e The event object
17041         */
17042         "click":true,
17043         /**
17044         * @event dblclick
17045         * Fires when this node is double clicked
17046         * @param {Node} this This node
17047         * @param {Roo.EventObject} e The event object
17048         */
17049         "dblclick":true,
17050         /**
17051         * @event contextmenu
17052         * Fires when this node is right clicked
17053         * @param {Node} this This node
17054         * @param {Roo.EventObject} e The event object
17055         */
17056         "contextmenu":true,
17057         /**
17058         * @event beforechildrenrendered
17059         * Fires right before the child nodes for this node are rendered
17060         * @param {Node} this This node
17061         */
17062         "beforechildrenrendered":true
17063     });
17064
17065     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17066
17067     /**
17068      * Read-only. The UI for this node
17069      * @type TreeNodeUI
17070      */
17071     this.ui = new uiClass(this);
17072 };
17073 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17074     preventHScroll: true,
17075     /**
17076      * Returns true if this node is expanded
17077      * @return {Boolean}
17078      */
17079     isExpanded : function(){
17080         return this.expanded;
17081     },
17082
17083     /**
17084      * Returns the UI object for this node
17085      * @return {TreeNodeUI}
17086      */
17087     getUI : function(){
17088         return this.ui;
17089     },
17090
17091     // private override
17092     setFirstChild : function(node){
17093         var of = this.firstChild;
17094         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17095         if(this.childrenRendered && of && node != of){
17096             of.renderIndent(true, true);
17097         }
17098         if(this.rendered){
17099             this.renderIndent(true, true);
17100         }
17101     },
17102
17103     // private override
17104     setLastChild : function(node){
17105         var ol = this.lastChild;
17106         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17107         if(this.childrenRendered && ol && node != ol){
17108             ol.renderIndent(true, true);
17109         }
17110         if(this.rendered){
17111             this.renderIndent(true, true);
17112         }
17113     },
17114
17115     // these methods are overridden to provide lazy rendering support
17116     // private override
17117     appendChild : function(){
17118         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17119         if(node && this.childrenRendered){
17120             node.render();
17121         }
17122         this.ui.updateExpandIcon();
17123         return node;
17124     },
17125
17126     // private override
17127     removeChild : function(node){
17128         this.ownerTree.getSelectionModel().unselect(node);
17129         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17130         // if it's been rendered remove dom node
17131         if(this.childrenRendered){
17132             node.ui.remove();
17133         }
17134         if(this.childNodes.length < 1){
17135             this.collapse(false, false);
17136         }else{
17137             this.ui.updateExpandIcon();
17138         }
17139         if(!this.firstChild) {
17140             this.childrenRendered = false;
17141         }
17142         return node;
17143     },
17144
17145     // private override
17146     insertBefore : function(node, refNode){
17147         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17148         if(newNode && refNode && this.childrenRendered){
17149             node.render();
17150         }
17151         this.ui.updateExpandIcon();
17152         return newNode;
17153     },
17154
17155     /**
17156      * Sets the text for this node
17157      * @param {String} text
17158      */
17159     setText : function(text){
17160         var oldText = this.text;
17161         this.text = text;
17162         this.attributes.text = text;
17163         if(this.rendered){ // event without subscribing
17164             this.ui.onTextChange(this, text, oldText);
17165         }
17166         this.fireEvent("textchange", this, text, oldText);
17167     },
17168
17169     /**
17170      * Triggers selection of this node
17171      */
17172     select : function(){
17173         this.getOwnerTree().getSelectionModel().select(this);
17174     },
17175
17176     /**
17177      * Triggers deselection of this node
17178      */
17179     unselect : function(){
17180         this.getOwnerTree().getSelectionModel().unselect(this);
17181     },
17182
17183     /**
17184      * Returns true if this node is selected
17185      * @return {Boolean}
17186      */
17187     isSelected : function(){
17188         return this.getOwnerTree().getSelectionModel().isSelected(this);
17189     },
17190
17191     /**
17192      * Expand this node.
17193      * @param {Boolean} deep (optional) True to expand all children as well
17194      * @param {Boolean} anim (optional) false to cancel the default animation
17195      * @param {Function} callback (optional) A callback to be called when
17196      * expanding this node completes (does not wait for deep expand to complete).
17197      * Called with 1 parameter, this node.
17198      */
17199     expand : function(deep, anim, callback){
17200         if(!this.expanded){
17201             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17202                 return;
17203             }
17204             if(!this.childrenRendered){
17205                 this.renderChildren();
17206             }
17207             this.expanded = true;
17208             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17209                 this.ui.animExpand(function(){
17210                     this.fireEvent("expand", this);
17211                     if(typeof callback == "function"){
17212                         callback(this);
17213                     }
17214                     if(deep === true){
17215                         this.expandChildNodes(true);
17216                     }
17217                 }.createDelegate(this));
17218                 return;
17219             }else{
17220                 this.ui.expand();
17221                 this.fireEvent("expand", this);
17222                 if(typeof callback == "function"){
17223                     callback(this);
17224                 }
17225             }
17226         }else{
17227            if(typeof callback == "function"){
17228                callback(this);
17229            }
17230         }
17231         if(deep === true){
17232             this.expandChildNodes(true);
17233         }
17234     },
17235
17236     isHiddenRoot : function(){
17237         return this.isRoot && !this.getOwnerTree().rootVisible;
17238     },
17239
17240     /**
17241      * Collapse this node.
17242      * @param {Boolean} deep (optional) True to collapse all children as well
17243      * @param {Boolean} anim (optional) false to cancel the default animation
17244      */
17245     collapse : function(deep, anim){
17246         if(this.expanded && !this.isHiddenRoot()){
17247             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17248                 return;
17249             }
17250             this.expanded = false;
17251             if((this.getOwnerTree().animate && anim !== false) || anim){
17252                 this.ui.animCollapse(function(){
17253                     this.fireEvent("collapse", this);
17254                     if(deep === true){
17255                         this.collapseChildNodes(true);
17256                     }
17257                 }.createDelegate(this));
17258                 return;
17259             }else{
17260                 this.ui.collapse();
17261                 this.fireEvent("collapse", this);
17262             }
17263         }
17264         if(deep === true){
17265             var cs = this.childNodes;
17266             for(var i = 0, len = cs.length; i < len; i++) {
17267                 cs[i].collapse(true, false);
17268             }
17269         }
17270     },
17271
17272     // private
17273     delayedExpand : function(delay){
17274         if(!this.expandProcId){
17275             this.expandProcId = this.expand.defer(delay, this);
17276         }
17277     },
17278
17279     // private
17280     cancelExpand : function(){
17281         if(this.expandProcId){
17282             clearTimeout(this.expandProcId);
17283         }
17284         this.expandProcId = false;
17285     },
17286
17287     /**
17288      * Toggles expanded/collapsed state of the node
17289      */
17290     toggle : function(){
17291         if(this.expanded){
17292             this.collapse();
17293         }else{
17294             this.expand();
17295         }
17296     },
17297
17298     /**
17299      * Ensures all parent nodes are expanded
17300      */
17301     ensureVisible : function(callback){
17302         var tree = this.getOwnerTree();
17303         tree.expandPath(this.parentNode.getPath(), false, function(){
17304             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17305             Roo.callback(callback);
17306         }.createDelegate(this));
17307     },
17308
17309     /**
17310      * Expand all child nodes
17311      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17312      */
17313     expandChildNodes : function(deep){
17314         var cs = this.childNodes;
17315         for(var i = 0, len = cs.length; i < len; i++) {
17316                 cs[i].expand(deep);
17317         }
17318     },
17319
17320     /**
17321      * Collapse all child nodes
17322      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17323      */
17324     collapseChildNodes : function(deep){
17325         var cs = this.childNodes;
17326         for(var i = 0, len = cs.length; i < len; i++) {
17327                 cs[i].collapse(deep);
17328         }
17329     },
17330
17331     /**
17332      * Disables this node
17333      */
17334     disable : function(){
17335         this.disabled = true;
17336         this.unselect();
17337         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17338             this.ui.onDisableChange(this, true);
17339         }
17340         this.fireEvent("disabledchange", this, true);
17341     },
17342
17343     /**
17344      * Enables this node
17345      */
17346     enable : function(){
17347         this.disabled = false;
17348         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17349             this.ui.onDisableChange(this, false);
17350         }
17351         this.fireEvent("disabledchange", this, false);
17352     },
17353
17354     // private
17355     renderChildren : function(suppressEvent){
17356         if(suppressEvent !== false){
17357             this.fireEvent("beforechildrenrendered", this);
17358         }
17359         var cs = this.childNodes;
17360         for(var i = 0, len = cs.length; i < len; i++){
17361             cs[i].render(true);
17362         }
17363         this.childrenRendered = true;
17364     },
17365
17366     // private
17367     sort : function(fn, scope){
17368         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17369         if(this.childrenRendered){
17370             var cs = this.childNodes;
17371             for(var i = 0, len = cs.length; i < len; i++){
17372                 cs[i].render(true);
17373             }
17374         }
17375     },
17376
17377     // private
17378     render : function(bulkRender){
17379         this.ui.render(bulkRender);
17380         if(!this.rendered){
17381             this.rendered = true;
17382             if(this.expanded){
17383                 this.expanded = false;
17384                 this.expand(false, false);
17385             }
17386         }
17387     },
17388
17389     // private
17390     renderIndent : function(deep, refresh){
17391         if(refresh){
17392             this.ui.childIndent = null;
17393         }
17394         this.ui.renderIndent();
17395         if(deep === true && this.childrenRendered){
17396             var cs = this.childNodes;
17397             for(var i = 0, len = cs.length; i < len; i++){
17398                 cs[i].renderIndent(true, refresh);
17399             }
17400         }
17401     }
17402 });/*
17403  * Based on:
17404  * Ext JS Library 1.1.1
17405  * Copyright(c) 2006-2007, Ext JS, LLC.
17406  *
17407  * Originally Released Under LGPL - original licence link has changed is not relivant.
17408  *
17409  * Fork - LGPL
17410  * <script type="text/javascript">
17411  */
17412  
17413 /**
17414  * @class Roo.tree.AsyncTreeNode
17415  * @extends Roo.tree.TreeNode
17416  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17417  * @constructor
17418  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17419  */
17420  Roo.tree.AsyncTreeNode = function(config){
17421     this.loaded = false;
17422     this.loading = false;
17423     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17424     /**
17425     * @event beforeload
17426     * Fires before this node is loaded, return false to cancel
17427     * @param {Node} this This node
17428     */
17429     this.addEvents({'beforeload':true, 'load': true});
17430     /**
17431     * @event load
17432     * Fires when this node is loaded
17433     * @param {Node} this This node
17434     */
17435     /**
17436      * The loader used by this node (defaults to using the tree's defined loader)
17437      * @type TreeLoader
17438      * @property loader
17439      */
17440 };
17441 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17442     expand : function(deep, anim, callback){
17443         if(this.loading){ // if an async load is already running, waiting til it's done
17444             var timer;
17445             var f = function(){
17446                 if(!this.loading){ // done loading
17447                     clearInterval(timer);
17448                     this.expand(deep, anim, callback);
17449                 }
17450             }.createDelegate(this);
17451             timer = setInterval(f, 200);
17452             return;
17453         }
17454         if(!this.loaded){
17455             if(this.fireEvent("beforeload", this) === false){
17456                 return;
17457             }
17458             this.loading = true;
17459             this.ui.beforeLoad(this);
17460             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17461             if(loader){
17462                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17463                 return;
17464             }
17465         }
17466         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17467     },
17468     
17469     /**
17470      * Returns true if this node is currently loading
17471      * @return {Boolean}
17472      */
17473     isLoading : function(){
17474         return this.loading;  
17475     },
17476     
17477     loadComplete : function(deep, anim, callback){
17478         this.loading = false;
17479         this.loaded = true;
17480         this.ui.afterLoad(this);
17481         this.fireEvent("load", this);
17482         this.expand(deep, anim, callback);
17483     },
17484     
17485     /**
17486      * Returns true if this node has been loaded
17487      * @return {Boolean}
17488      */
17489     isLoaded : function(){
17490         return this.loaded;
17491     },
17492     
17493     hasChildNodes : function(){
17494         if(!this.isLeaf() && !this.loaded){
17495             return true;
17496         }else{
17497             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17498         }
17499     },
17500
17501     /**
17502      * Trigger a reload for this node
17503      * @param {Function} callback
17504      */
17505     reload : function(callback){
17506         this.collapse(false, false);
17507         while(this.firstChild){
17508             this.removeChild(this.firstChild);
17509         }
17510         this.childrenRendered = false;
17511         this.loaded = false;
17512         if(this.isHiddenRoot()){
17513             this.expanded = false;
17514         }
17515         this.expand(false, false, callback);
17516     }
17517 });/*
17518  * Based on:
17519  * Ext JS Library 1.1.1
17520  * Copyright(c) 2006-2007, Ext JS, LLC.
17521  *
17522  * Originally Released Under LGPL - original licence link has changed is not relivant.
17523  *
17524  * Fork - LGPL
17525  * <script type="text/javascript">
17526  */
17527  
17528 /**
17529  * @class Roo.tree.TreeNodeUI
17530  * @constructor
17531  * @param {Object} node The node to render
17532  * The TreeNode UI implementation is separate from the
17533  * tree implementation. Unless you are customizing the tree UI,
17534  * you should never have to use this directly.
17535  */
17536 Roo.tree.TreeNodeUI = function(node){
17537     this.node = node;
17538     this.rendered = false;
17539     this.animating = false;
17540     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17541 };
17542
17543 Roo.tree.TreeNodeUI.prototype = {
17544     removeChild : function(node){
17545         if(this.rendered){
17546             this.ctNode.removeChild(node.ui.getEl());
17547         }
17548     },
17549
17550     beforeLoad : function(){
17551          this.addClass("x-tree-node-loading");
17552     },
17553
17554     afterLoad : function(){
17555          this.removeClass("x-tree-node-loading");
17556     },
17557
17558     onTextChange : function(node, text, oldText){
17559         if(this.rendered){
17560             this.textNode.innerHTML = text;
17561         }
17562     },
17563
17564     onDisableChange : function(node, state){
17565         this.disabled = state;
17566         if(state){
17567             this.addClass("x-tree-node-disabled");
17568         }else{
17569             this.removeClass("x-tree-node-disabled");
17570         }
17571     },
17572
17573     onSelectedChange : function(state){
17574         if(state){
17575             this.focus();
17576             this.addClass("x-tree-selected");
17577         }else{
17578             //this.blur();
17579             this.removeClass("x-tree-selected");
17580         }
17581     },
17582
17583     onMove : function(tree, node, oldParent, newParent, index, refNode){
17584         this.childIndent = null;
17585         if(this.rendered){
17586             var targetNode = newParent.ui.getContainer();
17587             if(!targetNode){//target not rendered
17588                 this.holder = document.createElement("div");
17589                 this.holder.appendChild(this.wrap);
17590                 return;
17591             }
17592             var insertBefore = refNode ? refNode.ui.getEl() : null;
17593             if(insertBefore){
17594                 targetNode.insertBefore(this.wrap, insertBefore);
17595             }else{
17596                 targetNode.appendChild(this.wrap);
17597             }
17598             this.node.renderIndent(true);
17599         }
17600     },
17601
17602     addClass : function(cls){
17603         if(this.elNode){
17604             Roo.fly(this.elNode).addClass(cls);
17605         }
17606     },
17607
17608     removeClass : function(cls){
17609         if(this.elNode){
17610             Roo.fly(this.elNode).removeClass(cls);
17611         }
17612     },
17613
17614     remove : function(){
17615         if(this.rendered){
17616             this.holder = document.createElement("div");
17617             this.holder.appendChild(this.wrap);
17618         }
17619     },
17620
17621     fireEvent : function(){
17622         return this.node.fireEvent.apply(this.node, arguments);
17623     },
17624
17625     initEvents : function(){
17626         this.node.on("move", this.onMove, this);
17627         var E = Roo.EventManager;
17628         var a = this.anchor;
17629
17630         var el = Roo.fly(a, '_treeui');
17631
17632         if(Roo.isOpera){ // opera render bug ignores the CSS
17633             el.setStyle("text-decoration", "none");
17634         }
17635
17636         el.on("click", this.onClick, this);
17637         el.on("dblclick", this.onDblClick, this);
17638
17639         if(this.checkbox){
17640             Roo.EventManager.on(this.checkbox,
17641                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17642         }
17643
17644         el.on("contextmenu", this.onContextMenu, this);
17645
17646         var icon = Roo.fly(this.iconNode);
17647         icon.on("click", this.onClick, this);
17648         icon.on("dblclick", this.onDblClick, this);
17649         icon.on("contextmenu", this.onContextMenu, this);
17650         E.on(this.ecNode, "click", this.ecClick, this, true);
17651
17652         if(this.node.disabled){
17653             this.addClass("x-tree-node-disabled");
17654         }
17655         if(this.node.hidden){
17656             this.addClass("x-tree-node-disabled");
17657         }
17658         var ot = this.node.getOwnerTree();
17659         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17660         if(dd && (!this.node.isRoot || ot.rootVisible)){
17661             Roo.dd.Registry.register(this.elNode, {
17662                 node: this.node,
17663                 handles: this.getDDHandles(),
17664                 isHandle: false
17665             });
17666         }
17667     },
17668
17669     getDDHandles : function(){
17670         return [this.iconNode, this.textNode];
17671     },
17672
17673     hide : function(){
17674         if(this.rendered){
17675             this.wrap.style.display = "none";
17676         }
17677     },
17678
17679     show : function(){
17680         if(this.rendered){
17681             this.wrap.style.display = "";
17682         }
17683     },
17684
17685     onContextMenu : function(e){
17686         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17687             e.preventDefault();
17688             this.focus();
17689             this.fireEvent("contextmenu", this.node, e);
17690         }
17691     },
17692
17693     onClick : function(e){
17694         if(this.dropping){
17695             e.stopEvent();
17696             return;
17697         }
17698         if(this.fireEvent("beforeclick", this.node, e) !== false){
17699             if(!this.disabled && this.node.attributes.href){
17700                 this.fireEvent("click", this.node, e);
17701                 return;
17702             }
17703             e.preventDefault();
17704             if(this.disabled){
17705                 return;
17706             }
17707
17708             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17709                 this.node.toggle();
17710             }
17711
17712             this.fireEvent("click", this.node, e);
17713         }else{
17714             e.stopEvent();
17715         }
17716     },
17717
17718     onDblClick : function(e){
17719         e.preventDefault();
17720         if(this.disabled){
17721             return;
17722         }
17723         if(this.checkbox){
17724             this.toggleCheck();
17725         }
17726         if(!this.animating && this.node.hasChildNodes()){
17727             this.node.toggle();
17728         }
17729         this.fireEvent("dblclick", this.node, e);
17730     },
17731
17732     onCheckChange : function(){
17733         var checked = this.checkbox.checked;
17734         this.node.attributes.checked = checked;
17735         this.fireEvent('checkchange', this.node, checked);
17736     },
17737
17738     ecClick : function(e){
17739         if(!this.animating && this.node.hasChildNodes()){
17740             this.node.toggle();
17741         }
17742     },
17743
17744     startDrop : function(){
17745         this.dropping = true;
17746     },
17747
17748     // delayed drop so the click event doesn't get fired on a drop
17749     endDrop : function(){
17750        setTimeout(function(){
17751            this.dropping = false;
17752        }.createDelegate(this), 50);
17753     },
17754
17755     expand : function(){
17756         this.updateExpandIcon();
17757         this.ctNode.style.display = "";
17758     },
17759
17760     focus : function(){
17761         if(!this.node.preventHScroll){
17762             try{this.anchor.focus();
17763             }catch(e){}
17764         }else if(!Roo.isIE){
17765             try{
17766                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17767                 var l = noscroll.scrollLeft;
17768                 this.anchor.focus();
17769                 noscroll.scrollLeft = l;
17770             }catch(e){}
17771         }
17772     },
17773
17774     toggleCheck : function(value){
17775         var cb = this.checkbox;
17776         if(cb){
17777             cb.checked = (value === undefined ? !cb.checked : value);
17778         }
17779     },
17780
17781     blur : function(){
17782         try{
17783             this.anchor.blur();
17784         }catch(e){}
17785     },
17786
17787     animExpand : function(callback){
17788         var ct = Roo.get(this.ctNode);
17789         ct.stopFx();
17790         if(!this.node.hasChildNodes()){
17791             this.updateExpandIcon();
17792             this.ctNode.style.display = "";
17793             Roo.callback(callback);
17794             return;
17795         }
17796         this.animating = true;
17797         this.updateExpandIcon();
17798
17799         ct.slideIn('t', {
17800            callback : function(){
17801                this.animating = false;
17802                Roo.callback(callback);
17803             },
17804             scope: this,
17805             duration: this.node.ownerTree.duration || .25
17806         });
17807     },
17808
17809     highlight : function(){
17810         var tree = this.node.getOwnerTree();
17811         Roo.fly(this.wrap).highlight(
17812             tree.hlColor || "C3DAF9",
17813             {endColor: tree.hlBaseColor}
17814         );
17815     },
17816
17817     collapse : function(){
17818         this.updateExpandIcon();
17819         this.ctNode.style.display = "none";
17820     },
17821
17822     animCollapse : function(callback){
17823         var ct = Roo.get(this.ctNode);
17824         ct.enableDisplayMode('block');
17825         ct.stopFx();
17826
17827         this.animating = true;
17828         this.updateExpandIcon();
17829
17830         ct.slideOut('t', {
17831             callback : function(){
17832                this.animating = false;
17833                Roo.callback(callback);
17834             },
17835             scope: this,
17836             duration: this.node.ownerTree.duration || .25
17837         });
17838     },
17839
17840     getContainer : function(){
17841         return this.ctNode;
17842     },
17843
17844     getEl : function(){
17845         return this.wrap;
17846     },
17847
17848     appendDDGhost : function(ghostNode){
17849         ghostNode.appendChild(this.elNode.cloneNode(true));
17850     },
17851
17852     getDDRepairXY : function(){
17853         return Roo.lib.Dom.getXY(this.iconNode);
17854     },
17855
17856     onRender : function(){
17857         this.render();
17858     },
17859
17860     render : function(bulkRender){
17861         var n = this.node, a = n.attributes;
17862         var targetNode = n.parentNode ?
17863               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17864
17865         if(!this.rendered){
17866             this.rendered = true;
17867
17868             this.renderElements(n, a, targetNode, bulkRender);
17869
17870             if(a.qtip){
17871                if(this.textNode.setAttributeNS){
17872                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17873                    if(a.qtipTitle){
17874                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17875                    }
17876                }else{
17877                    this.textNode.setAttribute("ext:qtip", a.qtip);
17878                    if(a.qtipTitle){
17879                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17880                    }
17881                }
17882             }else if(a.qtipCfg){
17883                 a.qtipCfg.target = Roo.id(this.textNode);
17884                 Roo.QuickTips.register(a.qtipCfg);
17885             }
17886             this.initEvents();
17887             if(!this.node.expanded){
17888                 this.updateExpandIcon();
17889             }
17890         }else{
17891             if(bulkRender === true) {
17892                 targetNode.appendChild(this.wrap);
17893             }
17894         }
17895     },
17896
17897     renderElements : function(n, a, targetNode, bulkRender){
17898         // add some indent caching, this helps performance when rendering a large tree
17899         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17900         var t = n.getOwnerTree();
17901         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17902         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17903         var cb = typeof a.checked == 'boolean';
17904         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17905         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17906             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17907             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17908             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17909             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17910             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17911              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17912                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17913             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17914             "</li>"];
17915
17916         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17917             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17918                                 n.nextSibling.ui.getEl(), buf.join(""));
17919         }else{
17920             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17921         }
17922
17923         this.elNode = this.wrap.childNodes[0];
17924         this.ctNode = this.wrap.childNodes[1];
17925         var cs = this.elNode.childNodes;
17926         this.indentNode = cs[0];
17927         this.ecNode = cs[1];
17928         this.iconNode = cs[2];
17929         var index = 3;
17930         if(cb){
17931             this.checkbox = cs[3];
17932             index++;
17933         }
17934         this.anchor = cs[index];
17935         this.textNode = cs[index].firstChild;
17936     },
17937
17938     getAnchor : function(){
17939         return this.anchor;
17940     },
17941
17942     getTextEl : function(){
17943         return this.textNode;
17944     },
17945
17946     getIconEl : function(){
17947         return this.iconNode;
17948     },
17949
17950     isChecked : function(){
17951         return this.checkbox ? this.checkbox.checked : false;
17952     },
17953
17954     updateExpandIcon : function(){
17955         if(this.rendered){
17956             var n = this.node, c1, c2;
17957             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17958             var hasChild = n.hasChildNodes();
17959             if(hasChild){
17960                 if(n.expanded){
17961                     cls += "-minus";
17962                     c1 = "x-tree-node-collapsed";
17963                     c2 = "x-tree-node-expanded";
17964                 }else{
17965                     cls += "-plus";
17966                     c1 = "x-tree-node-expanded";
17967                     c2 = "x-tree-node-collapsed";
17968                 }
17969                 if(this.wasLeaf){
17970                     this.removeClass("x-tree-node-leaf");
17971                     this.wasLeaf = false;
17972                 }
17973                 if(this.c1 != c1 || this.c2 != c2){
17974                     Roo.fly(this.elNode).replaceClass(c1, c2);
17975                     this.c1 = c1; this.c2 = c2;
17976                 }
17977             }else{
17978                 if(!this.wasLeaf){
17979                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
17980                     delete this.c1;
17981                     delete this.c2;
17982                     this.wasLeaf = true;
17983                 }
17984             }
17985             var ecc = "x-tree-ec-icon "+cls;
17986             if(this.ecc != ecc){
17987                 this.ecNode.className = ecc;
17988                 this.ecc = ecc;
17989             }
17990         }
17991     },
17992
17993     getChildIndent : function(){
17994         if(!this.childIndent){
17995             var buf = [];
17996             var p = this.node;
17997             while(p){
17998                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
17999                     if(!p.isLast()) {
18000                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18001                     } else {
18002                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18003                     }
18004                 }
18005                 p = p.parentNode;
18006             }
18007             this.childIndent = buf.join("");
18008         }
18009         return this.childIndent;
18010     },
18011
18012     renderIndent : function(){
18013         if(this.rendered){
18014             var indent = "";
18015             var p = this.node.parentNode;
18016             if(p){
18017                 indent = p.ui.getChildIndent();
18018             }
18019             if(this.indentMarkup != indent){ // don't rerender if not required
18020                 this.indentNode.innerHTML = indent;
18021                 this.indentMarkup = indent;
18022             }
18023             this.updateExpandIcon();
18024         }
18025     }
18026 };
18027
18028 Roo.tree.RootTreeNodeUI = function(){
18029     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18030 };
18031 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18032     render : function(){
18033         if(!this.rendered){
18034             var targetNode = this.node.ownerTree.innerCt.dom;
18035             this.node.expanded = true;
18036             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18037             this.wrap = this.ctNode = targetNode.firstChild;
18038         }
18039     },
18040     collapse : function(){
18041     },
18042     expand : function(){
18043     }
18044 });/*
18045  * Based on:
18046  * Ext JS Library 1.1.1
18047  * Copyright(c) 2006-2007, Ext JS, LLC.
18048  *
18049  * Originally Released Under LGPL - original licence link has changed is not relivant.
18050  *
18051  * Fork - LGPL
18052  * <script type="text/javascript">
18053  */
18054 /**
18055  * @class Roo.tree.TreeLoader
18056  * @extends Roo.util.Observable
18057  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18058  * nodes from a specified URL. The response must be a javascript Array definition
18059  * who's elements are node definition objects. eg:
18060  * <pre><code>
18061    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18062     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18063 </code></pre>
18064  * <br><br>
18065  * A server request is sent, and child nodes are loaded only when a node is expanded.
18066  * The loading node's id is passed to the server under the parameter name "node" to
18067  * enable the server to produce the correct child nodes.
18068  * <br><br>
18069  * To pass extra parameters, an event handler may be attached to the "beforeload"
18070  * event, and the parameters specified in the TreeLoader's baseParams property:
18071  * <pre><code>
18072     myTreeLoader.on("beforeload", function(treeLoader, node) {
18073         this.baseParams.category = node.attributes.category;
18074     }, this);
18075 </code></pre><
18076  * This would pass an HTTP parameter called "category" to the server containing
18077  * the value of the Node's "category" attribute.
18078  * @constructor
18079  * Creates a new Treeloader.
18080  * @param {Object} config A config object containing config properties.
18081  */
18082 Roo.tree.TreeLoader = function(config){
18083     this.baseParams = {};
18084     this.requestMethod = "POST";
18085     Roo.apply(this, config);
18086
18087     this.addEvents({
18088     
18089         /**
18090          * @event beforeload
18091          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18092          * @param {Object} This TreeLoader object.
18093          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18094          * @param {Object} callback The callback function specified in the {@link #load} call.
18095          */
18096         beforeload : true,
18097         /**
18098          * @event load
18099          * Fires when the node has been successfuly loaded.
18100          * @param {Object} This TreeLoader object.
18101          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18102          * @param {Object} response The response object containing the data from the server.
18103          */
18104         load : true,
18105         /**
18106          * @event loadexception
18107          * Fires if the network request failed.
18108          * @param {Object} This TreeLoader object.
18109          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18110          * @param {Object} response The response object containing the data from the server.
18111          */
18112         loadexception : true,
18113         /**
18114          * @event create
18115          * Fires before a node is created, enabling you to return custom Node types 
18116          * @param {Object} This TreeLoader object.
18117          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18118          */
18119         create : true
18120     });
18121
18122     Roo.tree.TreeLoader.superclass.constructor.call(this);
18123 };
18124
18125 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18126     /**
18127     * @cfg {String} dataUrl The URL from which to request a Json string which
18128     * specifies an array of node definition object representing the child nodes
18129     * to be loaded.
18130     */
18131     /**
18132     * @cfg {Object} baseParams (optional) An object containing properties which
18133     * specify HTTP parameters to be passed to each request for child nodes.
18134     */
18135     /**
18136     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18137     * created by this loader. If the attributes sent by the server have an attribute in this object,
18138     * they take priority.
18139     */
18140     /**
18141     * @cfg {Object} uiProviders (optional) An object containing properties which
18142     * 
18143     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18144     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18145     * <i>uiProvider</i> attribute of a returned child node is a string rather
18146     * than a reference to a TreeNodeUI implementation, this that string value
18147     * is used as a property name in the uiProviders object. You can define the provider named
18148     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18149     */
18150     uiProviders : {},
18151
18152     /**
18153     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18154     * child nodes before loading.
18155     */
18156     clearOnLoad : true,
18157
18158     /**
18159     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18160     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18161     * Grid query { data : [ .....] }
18162     */
18163     
18164     root : false,
18165      /**
18166     * @cfg {String} queryParam (optional) 
18167     * Name of the query as it will be passed on the querystring (defaults to 'node')
18168     * eg. the request will be ?node=[id]
18169     */
18170     
18171     
18172     queryParam: false,
18173     
18174     /**
18175      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18176      * This is called automatically when a node is expanded, but may be used to reload
18177      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18178      * @param {Roo.tree.TreeNode} node
18179      * @param {Function} callback
18180      */
18181     load : function(node, callback){
18182         if(this.clearOnLoad){
18183             while(node.firstChild){
18184                 node.removeChild(node.firstChild);
18185             }
18186         }
18187         if(node.attributes.children){ // preloaded json children
18188             var cs = node.attributes.children;
18189             for(var i = 0, len = cs.length; i < len; i++){
18190                 node.appendChild(this.createNode(cs[i]));
18191             }
18192             if(typeof callback == "function"){
18193                 callback();
18194             }
18195         }else if(this.dataUrl){
18196             this.requestData(node, callback);
18197         }
18198     },
18199
18200     getParams: function(node){
18201         var buf = [], bp = this.baseParams;
18202         for(var key in bp){
18203             if(typeof bp[key] != "function"){
18204                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18205             }
18206         }
18207         var n = this.queryParam === false ? 'node' : this.queryParam;
18208         buf.push(n + "=", encodeURIComponent(node.id));
18209         return buf.join("");
18210     },
18211
18212     requestData : function(node, callback){
18213         if(this.fireEvent("beforeload", this, node, callback) !== false){
18214             this.transId = Roo.Ajax.request({
18215                 method:this.requestMethod,
18216                 url: this.dataUrl||this.url,
18217                 success: this.handleResponse,
18218                 failure: this.handleFailure,
18219                 scope: this,
18220                 argument: {callback: callback, node: node},
18221                 params: this.getParams(node)
18222             });
18223         }else{
18224             // if the load is cancelled, make sure we notify
18225             // the node that we are done
18226             if(typeof callback == "function"){
18227                 callback();
18228             }
18229         }
18230     },
18231
18232     isLoading : function(){
18233         return this.transId ? true : false;
18234     },
18235
18236     abort : function(){
18237         if(this.isLoading()){
18238             Roo.Ajax.abort(this.transId);
18239         }
18240     },
18241
18242     // private
18243     createNode : function(attr){
18244         // apply baseAttrs, nice idea Corey!
18245         if(this.baseAttrs){
18246             Roo.applyIf(attr, this.baseAttrs);
18247         }
18248         if(this.applyLoader !== false){
18249             attr.loader = this;
18250         }
18251         // uiProvider = depreciated..
18252         
18253         if(typeof(attr.uiProvider) == 'string'){
18254            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18255                 /**  eval:var:attr */ eval(attr.uiProvider);
18256         }
18257         if(typeof(this.uiProviders['default']) != 'undefined') {
18258             attr.uiProvider = this.uiProviders['default'];
18259         }
18260         
18261         this.fireEvent('create', this, attr);
18262         
18263         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18264         return(attr.leaf ?
18265                         new Roo.tree.TreeNode(attr) :
18266                         new Roo.tree.AsyncTreeNode(attr));
18267     },
18268
18269     processResponse : function(response, node, callback){
18270         var json = response.responseText;
18271         try {
18272             
18273             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18274             if (this.root !== false) {
18275                 o = o[this.root];
18276             }
18277             
18278             for(var i = 0, len = o.length; i < len; i++){
18279                 var n = this.createNode(o[i]);
18280                 if(n){
18281                     node.appendChild(n);
18282                 }
18283             }
18284             if(typeof callback == "function"){
18285                 callback(this, node);
18286             }
18287         }catch(e){
18288             this.handleFailure(response);
18289         }
18290     },
18291
18292     handleResponse : function(response){
18293         this.transId = false;
18294         var a = response.argument;
18295         this.processResponse(response, a.node, a.callback);
18296         this.fireEvent("load", this, a.node, response);
18297     },
18298
18299     handleFailure : function(response){
18300         this.transId = false;
18301         var a = response.argument;
18302         this.fireEvent("loadexception", this, a.node, response);
18303         if(typeof a.callback == "function"){
18304             a.callback(this, a.node);
18305         }
18306     }
18307 });/*
18308  * Based on:
18309  * Ext JS Library 1.1.1
18310  * Copyright(c) 2006-2007, Ext JS, LLC.
18311  *
18312  * Originally Released Under LGPL - original licence link has changed is not relivant.
18313  *
18314  * Fork - LGPL
18315  * <script type="text/javascript">
18316  */
18317
18318 /**
18319 * @class Roo.tree.TreeFilter
18320 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18321 * @param {TreePanel} tree
18322 * @param {Object} config (optional)
18323  */
18324 Roo.tree.TreeFilter = function(tree, config){
18325     this.tree = tree;
18326     this.filtered = {};
18327     Roo.apply(this, config);
18328 };
18329
18330 Roo.tree.TreeFilter.prototype = {
18331     clearBlank:false,
18332     reverse:false,
18333     autoClear:false,
18334     remove:false,
18335
18336      /**
18337      * Filter the data by a specific attribute.
18338      * @param {String/RegExp} value Either string that the attribute value
18339      * should start with or a RegExp to test against the attribute
18340      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18341      * @param {TreeNode} startNode (optional) The node to start the filter at.
18342      */
18343     filter : function(value, attr, startNode){
18344         attr = attr || "text";
18345         var f;
18346         if(typeof value == "string"){
18347             var vlen = value.length;
18348             // auto clear empty filter
18349             if(vlen == 0 && this.clearBlank){
18350                 this.clear();
18351                 return;
18352             }
18353             value = value.toLowerCase();
18354             f = function(n){
18355                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18356             };
18357         }else if(value.exec){ // regex?
18358             f = function(n){
18359                 return value.test(n.attributes[attr]);
18360             };
18361         }else{
18362             throw 'Illegal filter type, must be string or regex';
18363         }
18364         this.filterBy(f, null, startNode);
18365         },
18366
18367     /**
18368      * Filter by a function. The passed function will be called with each
18369      * node in the tree (or from the startNode). If the function returns true, the node is kept
18370      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18371      * @param {Function} fn The filter function
18372      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18373      */
18374     filterBy : function(fn, scope, startNode){
18375         startNode = startNode || this.tree.root;
18376         if(this.autoClear){
18377             this.clear();
18378         }
18379         var af = this.filtered, rv = this.reverse;
18380         var f = function(n){
18381             if(n == startNode){
18382                 return true;
18383             }
18384             if(af[n.id]){
18385                 return false;
18386             }
18387             var m = fn.call(scope || n, n);
18388             if(!m || rv){
18389                 af[n.id] = n;
18390                 n.ui.hide();
18391                 return false;
18392             }
18393             return true;
18394         };
18395         startNode.cascade(f);
18396         if(this.remove){
18397            for(var id in af){
18398                if(typeof id != "function"){
18399                    var n = af[id];
18400                    if(n && n.parentNode){
18401                        n.parentNode.removeChild(n);
18402                    }
18403                }
18404            }
18405         }
18406     },
18407
18408     /**
18409      * Clears the current filter. Note: with the "remove" option
18410      * set a filter cannot be cleared.
18411      */
18412     clear : function(){
18413         var t = this.tree;
18414         var af = this.filtered;
18415         for(var id in af){
18416             if(typeof id != "function"){
18417                 var n = af[id];
18418                 if(n){
18419                     n.ui.show();
18420                 }
18421             }
18422         }
18423         this.filtered = {};
18424     }
18425 };
18426 /*
18427  * Based on:
18428  * Ext JS Library 1.1.1
18429  * Copyright(c) 2006-2007, Ext JS, LLC.
18430  *
18431  * Originally Released Under LGPL - original licence link has changed is not relivant.
18432  *
18433  * Fork - LGPL
18434  * <script type="text/javascript">
18435  */
18436  
18437
18438 /**
18439  * @class Roo.tree.TreeSorter
18440  * Provides sorting of nodes in a TreePanel
18441  * 
18442  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18443  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18444  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18445  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18446  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18447  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18448  * @constructor
18449  * @param {TreePanel} tree
18450  * @param {Object} config
18451  */
18452 Roo.tree.TreeSorter = function(tree, config){
18453     Roo.apply(this, config);
18454     tree.on("beforechildrenrendered", this.doSort, this);
18455     tree.on("append", this.updateSort, this);
18456     tree.on("insert", this.updateSort, this);
18457     
18458     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18459     var p = this.property || "text";
18460     var sortType = this.sortType;
18461     var fs = this.folderSort;
18462     var cs = this.caseSensitive === true;
18463     var leafAttr = this.leafAttr || 'leaf';
18464
18465     this.sortFn = function(n1, n2){
18466         if(fs){
18467             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18468                 return 1;
18469             }
18470             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18471                 return -1;
18472             }
18473         }
18474         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18475         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18476         if(v1 < v2){
18477                         return dsc ? +1 : -1;
18478                 }else if(v1 > v2){
18479                         return dsc ? -1 : +1;
18480         }else{
18481                 return 0;
18482         }
18483     };
18484 };
18485
18486 Roo.tree.TreeSorter.prototype = {
18487     doSort : function(node){
18488         node.sort(this.sortFn);
18489     },
18490     
18491     compareNodes : function(n1, n2){
18492         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18493     },
18494     
18495     updateSort : function(tree, node){
18496         if(node.childrenRendered){
18497             this.doSort.defer(1, this, [node]);
18498         }
18499     }
18500 };/*
18501  * Based on:
18502  * Ext JS Library 1.1.1
18503  * Copyright(c) 2006-2007, Ext JS, LLC.
18504  *
18505  * Originally Released Under LGPL - original licence link has changed is not relivant.
18506  *
18507  * Fork - LGPL
18508  * <script type="text/javascript">
18509  */
18510
18511 if(Roo.dd.DropZone){
18512     
18513 Roo.tree.TreeDropZone = function(tree, config){
18514     this.allowParentInsert = false;
18515     this.allowContainerDrop = false;
18516     this.appendOnly = false;
18517     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18518     this.tree = tree;
18519     this.lastInsertClass = "x-tree-no-status";
18520     this.dragOverData = {};
18521 };
18522
18523 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18524     ddGroup : "TreeDD",
18525     
18526     expandDelay : 1000,
18527     
18528     expandNode : function(node){
18529         if(node.hasChildNodes() && !node.isExpanded()){
18530             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18531         }
18532     },
18533     
18534     queueExpand : function(node){
18535         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18536     },
18537     
18538     cancelExpand : function(){
18539         if(this.expandProcId){
18540             clearTimeout(this.expandProcId);
18541             this.expandProcId = false;
18542         }
18543     },
18544     
18545     isValidDropPoint : function(n, pt, dd, e, data){
18546         if(!n || !data){ return false; }
18547         var targetNode = n.node;
18548         var dropNode = data.node;
18549         // default drop rules
18550         if(!(targetNode && targetNode.isTarget && pt)){
18551             return false;
18552         }
18553         if(pt == "append" && targetNode.allowChildren === false){
18554             return false;
18555         }
18556         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18557             return false;
18558         }
18559         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18560             return false;
18561         }
18562         // reuse the object
18563         var overEvent = this.dragOverData;
18564         overEvent.tree = this.tree;
18565         overEvent.target = targetNode;
18566         overEvent.data = data;
18567         overEvent.point = pt;
18568         overEvent.source = dd;
18569         overEvent.rawEvent = e;
18570         overEvent.dropNode = dropNode;
18571         overEvent.cancel = false;  
18572         var result = this.tree.fireEvent("nodedragover", overEvent);
18573         return overEvent.cancel === false && result !== false;
18574     },
18575     
18576     getDropPoint : function(e, n, dd){
18577         var tn = n.node;
18578         if(tn.isRoot){
18579             return tn.allowChildren !== false ? "append" : false; // always append for root
18580         }
18581         var dragEl = n.ddel;
18582         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18583         var y = Roo.lib.Event.getPageY(e);
18584         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18585         
18586         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18587         var noAppend = tn.allowChildren === false;
18588         if(this.appendOnly || tn.parentNode.allowChildren === false){
18589             return noAppend ? false : "append";
18590         }
18591         var noBelow = false;
18592         if(!this.allowParentInsert){
18593             noBelow = tn.hasChildNodes() && tn.isExpanded();
18594         }
18595         var q = (b - t) / (noAppend ? 2 : 3);
18596         if(y >= t && y < (t + q)){
18597             return "above";
18598         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18599             return "below";
18600         }else{
18601             return "append";
18602         }
18603     },
18604     
18605     onNodeEnter : function(n, dd, e, data){
18606         this.cancelExpand();
18607     },
18608     
18609     onNodeOver : function(n, dd, e, data){
18610         var pt = this.getDropPoint(e, n, dd);
18611         var node = n.node;
18612         
18613         // auto node expand check
18614         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18615             this.queueExpand(node);
18616         }else if(pt != "append"){
18617             this.cancelExpand();
18618         }
18619         
18620         // set the insert point style on the target node
18621         var returnCls = this.dropNotAllowed;
18622         if(this.isValidDropPoint(n, pt, dd, e, data)){
18623            if(pt){
18624                var el = n.ddel;
18625                var cls;
18626                if(pt == "above"){
18627                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18628                    cls = "x-tree-drag-insert-above";
18629                }else if(pt == "below"){
18630                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18631                    cls = "x-tree-drag-insert-below";
18632                }else{
18633                    returnCls = "x-tree-drop-ok-append";
18634                    cls = "x-tree-drag-append";
18635                }
18636                if(this.lastInsertClass != cls){
18637                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18638                    this.lastInsertClass = cls;
18639                }
18640            }
18641        }
18642        return returnCls;
18643     },
18644     
18645     onNodeOut : function(n, dd, e, data){
18646         this.cancelExpand();
18647         this.removeDropIndicators(n);
18648     },
18649     
18650     onNodeDrop : function(n, dd, e, data){
18651         var point = this.getDropPoint(e, n, dd);
18652         var targetNode = n.node;
18653         targetNode.ui.startDrop();
18654         if(!this.isValidDropPoint(n, point, dd, e, data)){
18655             targetNode.ui.endDrop();
18656             return false;
18657         }
18658         // first try to find the drop node
18659         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18660         var dropEvent = {
18661             tree : this.tree,
18662             target: targetNode,
18663             data: data,
18664             point: point,
18665             source: dd,
18666             rawEvent: e,
18667             dropNode: dropNode,
18668             cancel: !dropNode   
18669         };
18670         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18671         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18672             targetNode.ui.endDrop();
18673             return false;
18674         }
18675         // allow target changing
18676         targetNode = dropEvent.target;
18677         if(point == "append" && !targetNode.isExpanded()){
18678             targetNode.expand(false, null, function(){
18679                 this.completeDrop(dropEvent);
18680             }.createDelegate(this));
18681         }else{
18682             this.completeDrop(dropEvent);
18683         }
18684         return true;
18685     },
18686     
18687     completeDrop : function(de){
18688         var ns = de.dropNode, p = de.point, t = de.target;
18689         if(!(ns instanceof Array)){
18690             ns = [ns];
18691         }
18692         var n;
18693         for(var i = 0, len = ns.length; i < len; i++){
18694             n = ns[i];
18695             if(p == "above"){
18696                 t.parentNode.insertBefore(n, t);
18697             }else if(p == "below"){
18698                 t.parentNode.insertBefore(n, t.nextSibling);
18699             }else{
18700                 t.appendChild(n);
18701             }
18702         }
18703         n.ui.focus();
18704         if(this.tree.hlDrop){
18705             n.ui.highlight();
18706         }
18707         t.ui.endDrop();
18708         this.tree.fireEvent("nodedrop", de);
18709     },
18710     
18711     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18712         if(this.tree.hlDrop){
18713             dropNode.ui.focus();
18714             dropNode.ui.highlight();
18715         }
18716         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18717     },
18718     
18719     getTree : function(){
18720         return this.tree;
18721     },
18722     
18723     removeDropIndicators : function(n){
18724         if(n && n.ddel){
18725             var el = n.ddel;
18726             Roo.fly(el).removeClass([
18727                     "x-tree-drag-insert-above",
18728                     "x-tree-drag-insert-below",
18729                     "x-tree-drag-append"]);
18730             this.lastInsertClass = "_noclass";
18731         }
18732     },
18733     
18734     beforeDragDrop : function(target, e, id){
18735         this.cancelExpand();
18736         return true;
18737     },
18738     
18739     afterRepair : function(data){
18740         if(data && Roo.enableFx){
18741             data.node.ui.highlight();
18742         }
18743         this.hideProxy();
18744     }    
18745 });
18746
18747 }
18748 /*
18749  * Based on:
18750  * Ext JS Library 1.1.1
18751  * Copyright(c) 2006-2007, Ext JS, LLC.
18752  *
18753  * Originally Released Under LGPL - original licence link has changed is not relivant.
18754  *
18755  * Fork - LGPL
18756  * <script type="text/javascript">
18757  */
18758  
18759
18760 if(Roo.dd.DragZone){
18761 Roo.tree.TreeDragZone = function(tree, config){
18762     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18763     this.tree = tree;
18764 };
18765
18766 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18767     ddGroup : "TreeDD",
18768     
18769     onBeforeDrag : function(data, e){
18770         var n = data.node;
18771         return n && n.draggable && !n.disabled;
18772     },
18773     
18774     onInitDrag : function(e){
18775         var data = this.dragData;
18776         this.tree.getSelectionModel().select(data.node);
18777         this.proxy.update("");
18778         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18779         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18780     },
18781     
18782     getRepairXY : function(e, data){
18783         return data.node.ui.getDDRepairXY();
18784     },
18785     
18786     onEndDrag : function(data, e){
18787         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18788     },
18789     
18790     onValidDrop : function(dd, e, id){
18791         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18792         this.hideProxy();
18793     },
18794     
18795     beforeInvalidDrop : function(e, id){
18796         // this scrolls the original position back into view
18797         var sm = this.tree.getSelectionModel();
18798         sm.clearSelections();
18799         sm.select(this.dragData.node);
18800     }
18801 });
18802 }/*
18803  * Based on:
18804  * Ext JS Library 1.1.1
18805  * Copyright(c) 2006-2007, Ext JS, LLC.
18806  *
18807  * Originally Released Under LGPL - original licence link has changed is not relivant.
18808  *
18809  * Fork - LGPL
18810  * <script type="text/javascript">
18811  */
18812 /**
18813  * @class Roo.tree.TreeEditor
18814  * @extends Roo.Editor
18815  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18816  * as the editor field.
18817  * @constructor
18818  * @param {TreePanel} tree
18819  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18820  */
18821 Roo.tree.TreeEditor = function(tree, config){
18822     config = config || {};
18823     var field = config.events ? config : new Roo.form.TextField(config);
18824     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18825
18826     this.tree = tree;
18827
18828     tree.on('beforeclick', this.beforeNodeClick, this);
18829     tree.getTreeEl().on('mousedown', this.hide, this);
18830     this.on('complete', this.updateNode, this);
18831     this.on('beforestartedit', this.fitToTree, this);
18832     this.on('startedit', this.bindScroll, this, {delay:10});
18833     this.on('specialkey', this.onSpecialKey, this);
18834 };
18835
18836 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18837     /**
18838      * @cfg {String} alignment
18839      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18840      */
18841     alignment: "l-l",
18842     // inherit
18843     autoSize: false,
18844     /**
18845      * @cfg {Boolean} hideEl
18846      * True to hide the bound element while the editor is displayed (defaults to false)
18847      */
18848     hideEl : false,
18849     /**
18850      * @cfg {String} cls
18851      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18852      */
18853     cls: "x-small-editor x-tree-editor",
18854     /**
18855      * @cfg {Boolean} shim
18856      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18857      */
18858     shim:false,
18859     // inherit
18860     shadow:"frame",
18861     /**
18862      * @cfg {Number} maxWidth
18863      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18864      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18865      * scroll and client offsets into account prior to each edit.
18866      */
18867     maxWidth: 250,
18868
18869     editDelay : 350,
18870
18871     // private
18872     fitToTree : function(ed, el){
18873         var td = this.tree.getTreeEl().dom, nd = el.dom;
18874         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18875             td.scrollLeft = nd.offsetLeft;
18876         }
18877         var w = Math.min(
18878                 this.maxWidth,
18879                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18880         this.setSize(w, '');
18881     },
18882
18883     // private
18884     triggerEdit : function(node){
18885         this.completeEdit();
18886         this.editNode = node;
18887         this.startEdit(node.ui.textNode, node.text);
18888     },
18889
18890     // private
18891     bindScroll : function(){
18892         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18893     },
18894
18895     // private
18896     beforeNodeClick : function(node, e){
18897         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18898         this.lastClick = new Date();
18899         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18900             e.stopEvent();
18901             this.triggerEdit(node);
18902             return false;
18903         }
18904     },
18905
18906     // private
18907     updateNode : function(ed, value){
18908         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18909         this.editNode.setText(value);
18910     },
18911
18912     // private
18913     onHide : function(){
18914         Roo.tree.TreeEditor.superclass.onHide.call(this);
18915         if(this.editNode){
18916             this.editNode.ui.focus();
18917         }
18918     },
18919
18920     // private
18921     onSpecialKey : function(field, e){
18922         var k = e.getKey();
18923         if(k == e.ESC){
18924             e.stopEvent();
18925             this.cancelEdit();
18926         }else if(k == e.ENTER && !e.hasModifier()){
18927             e.stopEvent();
18928             this.completeEdit();
18929         }
18930     }
18931 });//<Script type="text/javascript">
18932 /*
18933  * Based on:
18934  * Ext JS Library 1.1.1
18935  * Copyright(c) 2006-2007, Ext JS, LLC.
18936  *
18937  * Originally Released Under LGPL - original licence link has changed is not relivant.
18938  *
18939  * Fork - LGPL
18940  * <script type="text/javascript">
18941  */
18942  
18943 /**
18944  * Not documented??? - probably should be...
18945  */
18946
18947 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18948     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18949     
18950     renderElements : function(n, a, targetNode, bulkRender){
18951         //consel.log("renderElements?");
18952         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18953
18954         var t = n.getOwnerTree();
18955         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18956         
18957         var cols = t.columns;
18958         var bw = t.borderWidth;
18959         var c = cols[0];
18960         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18961          var cb = typeof a.checked == "boolean";
18962         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18963         var colcls = 'x-t-' + tid + '-c0';
18964         var buf = [
18965             '<li class="x-tree-node">',
18966             
18967                 
18968                 '<div class="x-tree-node-el ', a.cls,'">',
18969                     // extran...
18970                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
18971                 
18972                 
18973                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
18974                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
18975                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
18976                            (a.icon ? ' x-tree-node-inline-icon' : ''),
18977                            (a.iconCls ? ' '+a.iconCls : ''),
18978                            '" unselectable="on" />',
18979                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
18980                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
18981                              
18982                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18983                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18984                             '<span unselectable="on" qtip="' + tx + '">',
18985                              tx,
18986                              '</span></a>' ,
18987                     '</div>',
18988                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18989                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
18990                  ];
18991         for(var i = 1, len = cols.length; i < len; i++){
18992             c = cols[i];
18993             colcls = 'x-t-' + tid + '-c' +i;
18994             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18995             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
18996                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
18997                       "</div>");
18998          }
18999          
19000          buf.push(
19001             '</a>',
19002             '<div class="x-clear"></div></div>',
19003             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19004             "</li>");
19005         
19006         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19007             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19008                                 n.nextSibling.ui.getEl(), buf.join(""));
19009         }else{
19010             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19011         }
19012         var el = this.wrap.firstChild;
19013         this.elRow = el;
19014         this.elNode = el.firstChild;
19015         this.ranchor = el.childNodes[1];
19016         this.ctNode = this.wrap.childNodes[1];
19017         var cs = el.firstChild.childNodes;
19018         this.indentNode = cs[0];
19019         this.ecNode = cs[1];
19020         this.iconNode = cs[2];
19021         var index = 3;
19022         if(cb){
19023             this.checkbox = cs[3];
19024             index++;
19025         }
19026         this.anchor = cs[index];
19027         
19028         this.textNode = cs[index].firstChild;
19029         
19030         //el.on("click", this.onClick, this);
19031         //el.on("dblclick", this.onDblClick, this);
19032         
19033         
19034        // console.log(this);
19035     },
19036     initEvents : function(){
19037         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19038         
19039             
19040         var a = this.ranchor;
19041
19042         var el = Roo.get(a);
19043
19044         if(Roo.isOpera){ // opera render bug ignores the CSS
19045             el.setStyle("text-decoration", "none");
19046         }
19047
19048         el.on("click", this.onClick, this);
19049         el.on("dblclick", this.onDblClick, this);
19050         el.on("contextmenu", this.onContextMenu, this);
19051         
19052     },
19053     
19054     /*onSelectedChange : function(state){
19055         if(state){
19056             this.focus();
19057             this.addClass("x-tree-selected");
19058         }else{
19059             //this.blur();
19060             this.removeClass("x-tree-selected");
19061         }
19062     },*/
19063     addClass : function(cls){
19064         if(this.elRow){
19065             Roo.fly(this.elRow).addClass(cls);
19066         }
19067         
19068     },
19069     
19070     
19071     removeClass : function(cls){
19072         if(this.elRow){
19073             Roo.fly(this.elRow).removeClass(cls);
19074         }
19075     }
19076
19077     
19078     
19079 });//<Script type="text/javascript">
19080
19081 /*
19082  * Based on:
19083  * Ext JS Library 1.1.1
19084  * Copyright(c) 2006-2007, Ext JS, LLC.
19085  *
19086  * Originally Released Under LGPL - original licence link has changed is not relivant.
19087  *
19088  * Fork - LGPL
19089  * <script type="text/javascript">
19090  */
19091  
19092
19093 /**
19094  * @class Roo.tree.ColumnTree
19095  * @extends Roo.data.TreePanel
19096  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19097  * @cfg {int} borderWidth  compined right/left border allowance
19098  * @constructor
19099  * @param {String/HTMLElement/Element} el The container element
19100  * @param {Object} config
19101  */
19102 Roo.tree.ColumnTree =  function(el, config)
19103 {
19104    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19105    this.addEvents({
19106         /**
19107         * @event resize
19108         * Fire this event on a container when it resizes
19109         * @param {int} w Width
19110         * @param {int} h Height
19111         */
19112        "resize" : true
19113     });
19114     this.on('resize', this.onResize, this);
19115 };
19116
19117 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19118     //lines:false,
19119     
19120     
19121     borderWidth: Roo.isBorderBox ? 0 : 2, 
19122     headEls : false,
19123     
19124     render : function(){
19125         // add the header.....
19126        
19127         Roo.tree.ColumnTree.superclass.render.apply(this);
19128         
19129         this.el.addClass('x-column-tree');
19130         
19131         this.headers = this.el.createChild(
19132             {cls:'x-tree-headers'},this.innerCt.dom);
19133    
19134         var cols = this.columns, c;
19135         var totalWidth = 0;
19136         this.headEls = [];
19137         var  len = cols.length;
19138         for(var i = 0; i < len; i++){
19139              c = cols[i];
19140              totalWidth += c.width;
19141             this.headEls.push(this.headers.createChild({
19142                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19143                  cn: {
19144                      cls:'x-tree-hd-text',
19145                      html: c.header
19146                  },
19147                  style:'width:'+(c.width-this.borderWidth)+'px;'
19148              }));
19149         }
19150         this.headers.createChild({cls:'x-clear'});
19151         // prevent floats from wrapping when clipped
19152         this.headers.setWidth(totalWidth);
19153         //this.innerCt.setWidth(totalWidth);
19154         this.innerCt.setStyle({ overflow: 'auto' });
19155         this.onResize(this.width, this.height);
19156              
19157         
19158     },
19159     onResize : function(w,h)
19160     {
19161         this.height = h;
19162         this.width = w;
19163         // resize cols..
19164         this.innerCt.setWidth(this.width);
19165         this.innerCt.setHeight(this.height-20);
19166         
19167         // headers...
19168         var cols = this.columns, c;
19169         var totalWidth = 0;
19170         var expEl = false;
19171         var len = cols.length;
19172         for(var i = 0; i < len; i++){
19173             c = cols[i];
19174             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19175                 // it's the expander..
19176                 expEl  = this.headEls[i];
19177                 continue;
19178             }
19179             totalWidth += c.width;
19180             
19181         }
19182         if (expEl) {
19183             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19184         }
19185         this.headers.setWidth(w-20);
19186
19187         
19188         
19189         
19190     }
19191 });
19192 /*
19193  * Based on:
19194  * Ext JS Library 1.1.1
19195  * Copyright(c) 2006-2007, Ext JS, LLC.
19196  *
19197  * Originally Released Under LGPL - original licence link has changed is not relivant.
19198  *
19199  * Fork - LGPL
19200  * <script type="text/javascript">
19201  */
19202  
19203 /**
19204  * @class Roo.menu.Menu
19205  * @extends Roo.util.Observable
19206  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19207  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19208  * @constructor
19209  * Creates a new Menu
19210  * @param {Object} config Configuration options
19211  */
19212 Roo.menu.Menu = function(config){
19213     Roo.apply(this, config);
19214     this.id = this.id || Roo.id();
19215     this.addEvents({
19216         /**
19217          * @event beforeshow
19218          * Fires before this menu is displayed
19219          * @param {Roo.menu.Menu} this
19220          */
19221         beforeshow : true,
19222         /**
19223          * @event beforehide
19224          * Fires before this menu is hidden
19225          * @param {Roo.menu.Menu} this
19226          */
19227         beforehide : true,
19228         /**
19229          * @event show
19230          * Fires after this menu is displayed
19231          * @param {Roo.menu.Menu} this
19232          */
19233         show : true,
19234         /**
19235          * @event hide
19236          * Fires after this menu is hidden
19237          * @param {Roo.menu.Menu} this
19238          */
19239         hide : true,
19240         /**
19241          * @event click
19242          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19243          * @param {Roo.menu.Menu} this
19244          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19245          * @param {Roo.EventObject} e
19246          */
19247         click : true,
19248         /**
19249          * @event mouseover
19250          * Fires when the mouse is hovering over this menu
19251          * @param {Roo.menu.Menu} this
19252          * @param {Roo.EventObject} e
19253          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19254          */
19255         mouseover : true,
19256         /**
19257          * @event mouseout
19258          * Fires when the mouse exits this menu
19259          * @param {Roo.menu.Menu} this
19260          * @param {Roo.EventObject} e
19261          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19262          */
19263         mouseout : true,
19264         /**
19265          * @event itemclick
19266          * Fires when a menu item contained in this menu is clicked
19267          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19268          * @param {Roo.EventObject} e
19269          */
19270         itemclick: true
19271     });
19272     if (this.registerMenu) {
19273         Roo.menu.MenuMgr.register(this);
19274     }
19275     
19276     var mis = this.items;
19277     this.items = new Roo.util.MixedCollection();
19278     if(mis){
19279         this.add.apply(this, mis);
19280     }
19281 };
19282
19283 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19284     /**
19285      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19286      */
19287     minWidth : 120,
19288     /**
19289      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19290      * for bottom-right shadow (defaults to "sides")
19291      */
19292     shadow : "sides",
19293     /**
19294      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19295      * this menu (defaults to "tl-tr?")
19296      */
19297     subMenuAlign : "tl-tr?",
19298     /**
19299      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19300      * relative to its element of origin (defaults to "tl-bl?")
19301      */
19302     defaultAlign : "tl-bl?",
19303     /**
19304      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19305      */
19306     allowOtherMenus : false,
19307     /**
19308      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19309      */
19310     registerMenu : true,
19311
19312     hidden:true,
19313
19314     // private
19315     render : function(){
19316         if(this.el){
19317             return;
19318         }
19319         var el = this.el = new Roo.Layer({
19320             cls: "x-menu",
19321             shadow:this.shadow,
19322             constrain: false,
19323             parentEl: this.parentEl || document.body,
19324             zindex:15000
19325         });
19326
19327         this.keyNav = new Roo.menu.MenuNav(this);
19328
19329         if(this.plain){
19330             el.addClass("x-menu-plain");
19331         }
19332         if(this.cls){
19333             el.addClass(this.cls);
19334         }
19335         // generic focus element
19336         this.focusEl = el.createChild({
19337             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19338         });
19339         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19340         ul.on("click", this.onClick, this);
19341         ul.on("mouseover", this.onMouseOver, this);
19342         ul.on("mouseout", this.onMouseOut, this);
19343         this.items.each(function(item){
19344             var li = document.createElement("li");
19345             li.className = "x-menu-list-item";
19346             ul.dom.appendChild(li);
19347             item.render(li, this);
19348         }, this);
19349         this.ul = ul;
19350         this.autoWidth();
19351     },
19352
19353     // private
19354     autoWidth : function(){
19355         var el = this.el, ul = this.ul;
19356         if(!el){
19357             return;
19358         }
19359         var w = this.width;
19360         if(w){
19361             el.setWidth(w);
19362         }else if(Roo.isIE){
19363             el.setWidth(this.minWidth);
19364             var t = el.dom.offsetWidth; // force recalc
19365             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19366         }
19367     },
19368
19369     // private
19370     delayAutoWidth : function(){
19371         if(this.rendered){
19372             if(!this.awTask){
19373                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19374             }
19375             this.awTask.delay(20);
19376         }
19377     },
19378
19379     // private
19380     findTargetItem : function(e){
19381         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19382         if(t && t.menuItemId){
19383             return this.items.get(t.menuItemId);
19384         }
19385     },
19386
19387     // private
19388     onClick : function(e){
19389         var t;
19390         if(t = this.findTargetItem(e)){
19391             t.onClick(e);
19392             this.fireEvent("click", this, t, e);
19393         }
19394     },
19395
19396     // private
19397     setActiveItem : function(item, autoExpand){
19398         if(item != this.activeItem){
19399             if(this.activeItem){
19400                 this.activeItem.deactivate();
19401             }
19402             this.activeItem = item;
19403             item.activate(autoExpand);
19404         }else if(autoExpand){
19405             item.expandMenu();
19406         }
19407     },
19408
19409     // private
19410     tryActivate : function(start, step){
19411         var items = this.items;
19412         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19413             var item = items.get(i);
19414             if(!item.disabled && item.canActivate){
19415                 this.setActiveItem(item, false);
19416                 return item;
19417             }
19418         }
19419         return false;
19420     },
19421
19422     // private
19423     onMouseOver : function(e){
19424         var t;
19425         if(t = this.findTargetItem(e)){
19426             if(t.canActivate && !t.disabled){
19427                 this.setActiveItem(t, true);
19428             }
19429         }
19430         this.fireEvent("mouseover", this, e, t);
19431     },
19432
19433     // private
19434     onMouseOut : function(e){
19435         var t;
19436         if(t = this.findTargetItem(e)){
19437             if(t == this.activeItem && t.shouldDeactivate(e)){
19438                 this.activeItem.deactivate();
19439                 delete this.activeItem;
19440             }
19441         }
19442         this.fireEvent("mouseout", this, e, t);
19443     },
19444
19445     /**
19446      * Read-only.  Returns true if the menu is currently displayed, else false.
19447      * @type Boolean
19448      */
19449     isVisible : function(){
19450         return this.el && !this.hidden;
19451     },
19452
19453     /**
19454      * Displays this menu relative to another element
19455      * @param {String/HTMLElement/Roo.Element} element The element to align to
19456      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19457      * the element (defaults to this.defaultAlign)
19458      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19459      */
19460     show : function(el, pos, parentMenu){
19461         this.parentMenu = parentMenu;
19462         if(!this.el){
19463             this.render();
19464         }
19465         this.fireEvent("beforeshow", this);
19466         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19467     },
19468
19469     /**
19470      * Displays this menu at a specific xy position
19471      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19472      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19473      */
19474     showAt : function(xy, parentMenu, /* private: */_e){
19475         this.parentMenu = parentMenu;
19476         if(!this.el){
19477             this.render();
19478         }
19479         if(_e !== false){
19480             this.fireEvent("beforeshow", this);
19481             xy = this.el.adjustForConstraints(xy);
19482         }
19483         this.el.setXY(xy);
19484         this.el.show();
19485         this.hidden = false;
19486         this.focus();
19487         this.fireEvent("show", this);
19488     },
19489
19490     focus : function(){
19491         if(!this.hidden){
19492             this.doFocus.defer(50, this);
19493         }
19494     },
19495
19496     doFocus : function(){
19497         if(!this.hidden){
19498             this.focusEl.focus();
19499         }
19500     },
19501
19502     /**
19503      * Hides this menu and optionally all parent menus
19504      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19505      */
19506     hide : function(deep){
19507         if(this.el && this.isVisible()){
19508             this.fireEvent("beforehide", this);
19509             if(this.activeItem){
19510                 this.activeItem.deactivate();
19511                 this.activeItem = null;
19512             }
19513             this.el.hide();
19514             this.hidden = true;
19515             this.fireEvent("hide", this);
19516         }
19517         if(deep === true && this.parentMenu){
19518             this.parentMenu.hide(true);
19519         }
19520     },
19521
19522     /**
19523      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19524      * Any of the following are valid:
19525      * <ul>
19526      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19527      * <li>An HTMLElement object which will be converted to a menu item</li>
19528      * <li>A menu item config object that will be created as a new menu item</li>
19529      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19530      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19531      * </ul>
19532      * Usage:
19533      * <pre><code>
19534 // Create the menu
19535 var menu = new Roo.menu.Menu();
19536
19537 // Create a menu item to add by reference
19538 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19539
19540 // Add a bunch of items at once using different methods.
19541 // Only the last item added will be returned.
19542 var item = menu.add(
19543     menuItem,                // add existing item by ref
19544     'Dynamic Item',          // new TextItem
19545     '-',                     // new separator
19546     { text: 'Config Item' }  // new item by config
19547 );
19548 </code></pre>
19549      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19550      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19551      */
19552     add : function(){
19553         var a = arguments, l = a.length, item;
19554         for(var i = 0; i < l; i++){
19555             var el = a[i];
19556             if ((typeof(el) == "object") && el.xtype && el.xns) {
19557                 el = Roo.factory(el, Roo.menu);
19558             }
19559             
19560             if(el.render){ // some kind of Item
19561                 item = this.addItem(el);
19562             }else if(typeof el == "string"){ // string
19563                 if(el == "separator" || el == "-"){
19564                     item = this.addSeparator();
19565                 }else{
19566                     item = this.addText(el);
19567                 }
19568             }else if(el.tagName || el.el){ // element
19569                 item = this.addElement(el);
19570             }else if(typeof el == "object"){ // must be menu item config?
19571                 item = this.addMenuItem(el);
19572             }
19573         }
19574         return item;
19575     },
19576
19577     /**
19578      * Returns this menu's underlying {@link Roo.Element} object
19579      * @return {Roo.Element} The element
19580      */
19581     getEl : function(){
19582         if(!this.el){
19583             this.render();
19584         }
19585         return this.el;
19586     },
19587
19588     /**
19589      * Adds a separator bar to the menu
19590      * @return {Roo.menu.Item} The menu item that was added
19591      */
19592     addSeparator : function(){
19593         return this.addItem(new Roo.menu.Separator());
19594     },
19595
19596     /**
19597      * Adds an {@link Roo.Element} object to the menu
19598      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19599      * @return {Roo.menu.Item} The menu item that was added
19600      */
19601     addElement : function(el){
19602         return this.addItem(new Roo.menu.BaseItem(el));
19603     },
19604
19605     /**
19606      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19607      * @param {Roo.menu.Item} item The menu item to add
19608      * @return {Roo.menu.Item} The menu item that was added
19609      */
19610     addItem : function(item){
19611         this.items.add(item);
19612         if(this.ul){
19613             var li = document.createElement("li");
19614             li.className = "x-menu-list-item";
19615             this.ul.dom.appendChild(li);
19616             item.render(li, this);
19617             this.delayAutoWidth();
19618         }
19619         return item;
19620     },
19621
19622     /**
19623      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19624      * @param {Object} config A MenuItem config object
19625      * @return {Roo.menu.Item} The menu item that was added
19626      */
19627     addMenuItem : function(config){
19628         if(!(config instanceof Roo.menu.Item)){
19629             if(typeof config.checked == "boolean"){ // must be check menu item config?
19630                 config = new Roo.menu.CheckItem(config);
19631             }else{
19632                 config = new Roo.menu.Item(config);
19633             }
19634         }
19635         return this.addItem(config);
19636     },
19637
19638     /**
19639      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19640      * @param {String} text The text to display in the menu item
19641      * @return {Roo.menu.Item} The menu item that was added
19642      */
19643     addText : function(text){
19644         return this.addItem(new Roo.menu.TextItem({ text : text }));
19645     },
19646
19647     /**
19648      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19649      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19650      * @param {Roo.menu.Item} item The menu item to add
19651      * @return {Roo.menu.Item} The menu item that was added
19652      */
19653     insert : function(index, item){
19654         this.items.insert(index, item);
19655         if(this.ul){
19656             var li = document.createElement("li");
19657             li.className = "x-menu-list-item";
19658             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19659             item.render(li, this);
19660             this.delayAutoWidth();
19661         }
19662         return item;
19663     },
19664
19665     /**
19666      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19667      * @param {Roo.menu.Item} item The menu item to remove
19668      */
19669     remove : function(item){
19670         this.items.removeKey(item.id);
19671         item.destroy();
19672     },
19673
19674     /**
19675      * Removes and destroys all items in the menu
19676      */
19677     removeAll : function(){
19678         var f;
19679         while(f = this.items.first()){
19680             this.remove(f);
19681         }
19682     }
19683 });
19684
19685 // MenuNav is a private utility class used internally by the Menu
19686 Roo.menu.MenuNav = function(menu){
19687     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19688     this.scope = this.menu = menu;
19689 };
19690
19691 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19692     doRelay : function(e, h){
19693         var k = e.getKey();
19694         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19695             this.menu.tryActivate(0, 1);
19696             return false;
19697         }
19698         return h.call(this.scope || this, e, this.menu);
19699     },
19700
19701     up : function(e, m){
19702         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19703             m.tryActivate(m.items.length-1, -1);
19704         }
19705     },
19706
19707     down : function(e, m){
19708         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19709             m.tryActivate(0, 1);
19710         }
19711     },
19712
19713     right : function(e, m){
19714         if(m.activeItem){
19715             m.activeItem.expandMenu(true);
19716         }
19717     },
19718
19719     left : function(e, m){
19720         m.hide();
19721         if(m.parentMenu && m.parentMenu.activeItem){
19722             m.parentMenu.activeItem.activate();
19723         }
19724     },
19725
19726     enter : function(e, m){
19727         if(m.activeItem){
19728             e.stopPropagation();
19729             m.activeItem.onClick(e);
19730             m.fireEvent("click", this, m.activeItem);
19731             return true;
19732         }
19733     }
19734 });/*
19735  * Based on:
19736  * Ext JS Library 1.1.1
19737  * Copyright(c) 2006-2007, Ext JS, LLC.
19738  *
19739  * Originally Released Under LGPL - original licence link has changed is not relivant.
19740  *
19741  * Fork - LGPL
19742  * <script type="text/javascript">
19743  */
19744  
19745 /**
19746  * @class Roo.menu.MenuMgr
19747  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19748  * @singleton
19749  */
19750 Roo.menu.MenuMgr = function(){
19751    var menus, active, groups = {}, attached = false, lastShow = new Date();
19752
19753    // private - called when first menu is created
19754    function init(){
19755        menus = {};
19756        active = new Roo.util.MixedCollection();
19757        Roo.get(document).addKeyListener(27, function(){
19758            if(active.length > 0){
19759                hideAll();
19760            }
19761        });
19762    }
19763
19764    // private
19765    function hideAll(){
19766        if(active && active.length > 0){
19767            var c = active.clone();
19768            c.each(function(m){
19769                m.hide();
19770            });
19771        }
19772    }
19773
19774    // private
19775    function onHide(m){
19776        active.remove(m);
19777        if(active.length < 1){
19778            Roo.get(document).un("mousedown", onMouseDown);
19779            attached = false;
19780        }
19781    }
19782
19783    // private
19784    function onShow(m){
19785        var last = active.last();
19786        lastShow = new Date();
19787        active.add(m);
19788        if(!attached){
19789            Roo.get(document).on("mousedown", onMouseDown);
19790            attached = true;
19791        }
19792        if(m.parentMenu){
19793           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19794           m.parentMenu.activeChild = m;
19795        }else if(last && last.isVisible()){
19796           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19797        }
19798    }
19799
19800    // private
19801    function onBeforeHide(m){
19802        if(m.activeChild){
19803            m.activeChild.hide();
19804        }
19805        if(m.autoHideTimer){
19806            clearTimeout(m.autoHideTimer);
19807            delete m.autoHideTimer;
19808        }
19809    }
19810
19811    // private
19812    function onBeforeShow(m){
19813        var pm = m.parentMenu;
19814        if(!pm && !m.allowOtherMenus){
19815            hideAll();
19816        }else if(pm && pm.activeChild && active != m){
19817            pm.activeChild.hide();
19818        }
19819    }
19820
19821    // private
19822    function onMouseDown(e){
19823        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19824            hideAll();
19825        }
19826    }
19827
19828    // private
19829    function onBeforeCheck(mi, state){
19830        if(state){
19831            var g = groups[mi.group];
19832            for(var i = 0, l = g.length; i < l; i++){
19833                if(g[i] != mi){
19834                    g[i].setChecked(false);
19835                }
19836            }
19837        }
19838    }
19839
19840    return {
19841
19842        /**
19843         * Hides all menus that are currently visible
19844         */
19845        hideAll : function(){
19846             hideAll();  
19847        },
19848
19849        // private
19850        register : function(menu){
19851            if(!menus){
19852                init();
19853            }
19854            menus[menu.id] = menu;
19855            menu.on("beforehide", onBeforeHide);
19856            menu.on("hide", onHide);
19857            menu.on("beforeshow", onBeforeShow);
19858            menu.on("show", onShow);
19859            var g = menu.group;
19860            if(g && menu.events["checkchange"]){
19861                if(!groups[g]){
19862                    groups[g] = [];
19863                }
19864                groups[g].push(menu);
19865                menu.on("checkchange", onCheck);
19866            }
19867        },
19868
19869         /**
19870          * Returns a {@link Roo.menu.Menu} object
19871          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19872          * be used to generate and return a new Menu instance.
19873          */
19874        get : function(menu){
19875            if(typeof menu == "string"){ // menu id
19876                return menus[menu];
19877            }else if(menu.events){  // menu instance
19878                return menu;
19879            }else if(typeof menu.length == 'number'){ // array of menu items?
19880                return new Roo.menu.Menu({items:menu});
19881            }else{ // otherwise, must be a config
19882                return new Roo.menu.Menu(menu);
19883            }
19884        },
19885
19886        // private
19887        unregister : function(menu){
19888            delete menus[menu.id];
19889            menu.un("beforehide", onBeforeHide);
19890            menu.un("hide", onHide);
19891            menu.un("beforeshow", onBeforeShow);
19892            menu.un("show", onShow);
19893            var g = menu.group;
19894            if(g && menu.events["checkchange"]){
19895                groups[g].remove(menu);
19896                menu.un("checkchange", onCheck);
19897            }
19898        },
19899
19900        // private
19901        registerCheckable : function(menuItem){
19902            var g = menuItem.group;
19903            if(g){
19904                if(!groups[g]){
19905                    groups[g] = [];
19906                }
19907                groups[g].push(menuItem);
19908                menuItem.on("beforecheckchange", onBeforeCheck);
19909            }
19910        },
19911
19912        // private
19913        unregisterCheckable : function(menuItem){
19914            var g = menuItem.group;
19915            if(g){
19916                groups[g].remove(menuItem);
19917                menuItem.un("beforecheckchange", onBeforeCheck);
19918            }
19919        }
19920    };
19921 }();/*
19922  * Based on:
19923  * Ext JS Library 1.1.1
19924  * Copyright(c) 2006-2007, Ext JS, LLC.
19925  *
19926  * Originally Released Under LGPL - original licence link has changed is not relivant.
19927  *
19928  * Fork - LGPL
19929  * <script type="text/javascript">
19930  */
19931  
19932
19933 /**
19934  * @class Roo.menu.BaseItem
19935  * @extends Roo.Component
19936  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19937  * management and base configuration options shared by all menu components.
19938  * @constructor
19939  * Creates a new BaseItem
19940  * @param {Object} config Configuration options
19941  */
19942 Roo.menu.BaseItem = function(config){
19943     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19944
19945     this.addEvents({
19946         /**
19947          * @event click
19948          * Fires when this item is clicked
19949          * @param {Roo.menu.BaseItem} this
19950          * @param {Roo.EventObject} e
19951          */
19952         click: true,
19953         /**
19954          * @event activate
19955          * Fires when this item is activated
19956          * @param {Roo.menu.BaseItem} this
19957          */
19958         activate : true,
19959         /**
19960          * @event deactivate
19961          * Fires when this item is deactivated
19962          * @param {Roo.menu.BaseItem} this
19963          */
19964         deactivate : true
19965     });
19966
19967     if(this.handler){
19968         this.on("click", this.handler, this.scope, true);
19969     }
19970 };
19971
19972 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
19973     /**
19974      * @cfg {Function} handler
19975      * A function that will handle the click event of this menu item (defaults to undefined)
19976      */
19977     /**
19978      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
19979      */
19980     canActivate : false,
19981     /**
19982      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
19983      */
19984     activeClass : "x-menu-item-active",
19985     /**
19986      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
19987      */
19988     hideOnClick : true,
19989     /**
19990      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
19991      */
19992     hideDelay : 100,
19993
19994     // private
19995     ctype: "Roo.menu.BaseItem",
19996
19997     // private
19998     actionMode : "container",
19999
20000     // private
20001     render : function(container, parentMenu){
20002         this.parentMenu = parentMenu;
20003         Roo.menu.BaseItem.superclass.render.call(this, container);
20004         this.container.menuItemId = this.id;
20005     },
20006
20007     // private
20008     onRender : function(container, position){
20009         this.el = Roo.get(this.el);
20010         container.dom.appendChild(this.el.dom);
20011     },
20012
20013     // private
20014     onClick : function(e){
20015         if(!this.disabled && this.fireEvent("click", this, e) !== false
20016                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20017             this.handleClick(e);
20018         }else{
20019             e.stopEvent();
20020         }
20021     },
20022
20023     // private
20024     activate : function(){
20025         if(this.disabled){
20026             return false;
20027         }
20028         var li = this.container;
20029         li.addClass(this.activeClass);
20030         this.region = li.getRegion().adjust(2, 2, -2, -2);
20031         this.fireEvent("activate", this);
20032         return true;
20033     },
20034
20035     // private
20036     deactivate : function(){
20037         this.container.removeClass(this.activeClass);
20038         this.fireEvent("deactivate", this);
20039     },
20040
20041     // private
20042     shouldDeactivate : function(e){
20043         return !this.region || !this.region.contains(e.getPoint());
20044     },
20045
20046     // private
20047     handleClick : function(e){
20048         if(this.hideOnClick){
20049             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20050         }
20051     },
20052
20053     // private
20054     expandMenu : function(autoActivate){
20055         // do nothing
20056     },
20057
20058     // private
20059     hideMenu : function(){
20060         // do nothing
20061     }
20062 });/*
20063  * Based on:
20064  * Ext JS Library 1.1.1
20065  * Copyright(c) 2006-2007, Ext JS, LLC.
20066  *
20067  * Originally Released Under LGPL - original licence link has changed is not relivant.
20068  *
20069  * Fork - LGPL
20070  * <script type="text/javascript">
20071  */
20072  
20073 /**
20074  * @class Roo.menu.Adapter
20075  * @extends Roo.menu.BaseItem
20076  * 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.
20077  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20078  * @constructor
20079  * Creates a new Adapter
20080  * @param {Object} config Configuration options
20081  */
20082 Roo.menu.Adapter = function(component, config){
20083     Roo.menu.Adapter.superclass.constructor.call(this, config);
20084     this.component = component;
20085 };
20086 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20087     // private
20088     canActivate : true,
20089
20090     // private
20091     onRender : function(container, position){
20092         this.component.render(container);
20093         this.el = this.component.getEl();
20094     },
20095
20096     // private
20097     activate : function(){
20098         if(this.disabled){
20099             return false;
20100         }
20101         this.component.focus();
20102         this.fireEvent("activate", this);
20103         return true;
20104     },
20105
20106     // private
20107     deactivate : function(){
20108         this.fireEvent("deactivate", this);
20109     },
20110
20111     // private
20112     disable : function(){
20113         this.component.disable();
20114         Roo.menu.Adapter.superclass.disable.call(this);
20115     },
20116
20117     // private
20118     enable : function(){
20119         this.component.enable();
20120         Roo.menu.Adapter.superclass.enable.call(this);
20121     }
20122 });/*
20123  * Based on:
20124  * Ext JS Library 1.1.1
20125  * Copyright(c) 2006-2007, Ext JS, LLC.
20126  *
20127  * Originally Released Under LGPL - original licence link has changed is not relivant.
20128  *
20129  * Fork - LGPL
20130  * <script type="text/javascript">
20131  */
20132
20133 /**
20134  * @class Roo.menu.TextItem
20135  * @extends Roo.menu.BaseItem
20136  * Adds a static text string to a menu, usually used as either a heading or group separator.
20137  * Note: old style constructor with text is still supported.
20138  * 
20139  * @constructor
20140  * Creates a new TextItem
20141  * @param {Object} cfg Configuration
20142  */
20143 Roo.menu.TextItem = function(cfg){
20144     if (typeof(cfg) == 'string') {
20145         this.text = cfg;
20146     } else {
20147         Roo.apply(this,cfg);
20148     }
20149     
20150     Roo.menu.TextItem.superclass.constructor.call(this);
20151 };
20152
20153 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20154     /**
20155      * @cfg {Boolean} text Text to show on item.
20156      */
20157     text : '',
20158     
20159     /**
20160      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20161      */
20162     hideOnClick : false,
20163     /**
20164      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20165      */
20166     itemCls : "x-menu-text",
20167
20168     // private
20169     onRender : function(){
20170         var s = document.createElement("span");
20171         s.className = this.itemCls;
20172         s.innerHTML = this.text;
20173         this.el = s;
20174         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20175     }
20176 });/*
20177  * Based on:
20178  * Ext JS Library 1.1.1
20179  * Copyright(c) 2006-2007, Ext JS, LLC.
20180  *
20181  * Originally Released Under LGPL - original licence link has changed is not relivant.
20182  *
20183  * Fork - LGPL
20184  * <script type="text/javascript">
20185  */
20186
20187 /**
20188  * @class Roo.menu.Separator
20189  * @extends Roo.menu.BaseItem
20190  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20191  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20192  * @constructor
20193  * @param {Object} config Configuration options
20194  */
20195 Roo.menu.Separator = function(config){
20196     Roo.menu.Separator.superclass.constructor.call(this, config);
20197 };
20198
20199 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20200     /**
20201      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20202      */
20203     itemCls : "x-menu-sep",
20204     /**
20205      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20206      */
20207     hideOnClick : false,
20208
20209     // private
20210     onRender : function(li){
20211         var s = document.createElement("span");
20212         s.className = this.itemCls;
20213         s.innerHTML = "&#160;";
20214         this.el = s;
20215         li.addClass("x-menu-sep-li");
20216         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20217     }
20218 });/*
20219  * Based on:
20220  * Ext JS Library 1.1.1
20221  * Copyright(c) 2006-2007, Ext JS, LLC.
20222  *
20223  * Originally Released Under LGPL - original licence link has changed is not relivant.
20224  *
20225  * Fork - LGPL
20226  * <script type="text/javascript">
20227  */
20228 /**
20229  * @class Roo.menu.Item
20230  * @extends Roo.menu.BaseItem
20231  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20232  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20233  * activation and click handling.
20234  * @constructor
20235  * Creates a new Item
20236  * @param {Object} config Configuration options
20237  */
20238 Roo.menu.Item = function(config){
20239     Roo.menu.Item.superclass.constructor.call(this, config);
20240     if(this.menu){
20241         this.menu = Roo.menu.MenuMgr.get(this.menu);
20242     }
20243 };
20244 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20245     
20246     /**
20247      * @cfg {String} text
20248      * The text to show on the menu item.
20249      */
20250     text: '',
20251      /**
20252      * @cfg {String} HTML to render in menu
20253      * The text to show on the menu item (HTML version).
20254      */
20255     html: '',
20256     /**
20257      * @cfg {String} icon
20258      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20259      */
20260     icon: undefined,
20261     /**
20262      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20263      */
20264     itemCls : "x-menu-item",
20265     /**
20266      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20267      */
20268     canActivate : true,
20269     /**
20270      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20271      */
20272     showDelay: 200,
20273     // doc'd in BaseItem
20274     hideDelay: 200,
20275
20276     // private
20277     ctype: "Roo.menu.Item",
20278     
20279     // private
20280     onRender : function(container, position){
20281         var el = document.createElement("a");
20282         el.hideFocus = true;
20283         el.unselectable = "on";
20284         el.href = this.href || "#";
20285         if(this.hrefTarget){
20286             el.target = this.hrefTarget;
20287         }
20288         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20289         
20290         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20291         
20292         el.innerHTML = String.format(
20293                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20294                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20295         this.el = el;
20296         Roo.menu.Item.superclass.onRender.call(this, container, position);
20297     },
20298
20299     /**
20300      * Sets the text to display in this menu item
20301      * @param {String} text The text to display
20302      * @param {Boolean} isHTML true to indicate text is pure html.
20303      */
20304     setText : function(text, isHTML){
20305         if (isHTML) {
20306             this.html = text;
20307         } else {
20308             this.text = text;
20309             this.html = '';
20310         }
20311         if(this.rendered){
20312             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20313      
20314             this.el.update(String.format(
20315                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20316                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20317             this.parentMenu.autoWidth();
20318         }
20319     },
20320
20321     // private
20322     handleClick : function(e){
20323         if(!this.href){ // if no link defined, stop the event automatically
20324             e.stopEvent();
20325         }
20326         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20327     },
20328
20329     // private
20330     activate : function(autoExpand){
20331         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20332             this.focus();
20333             if(autoExpand){
20334                 this.expandMenu();
20335             }
20336         }
20337         return true;
20338     },
20339
20340     // private
20341     shouldDeactivate : function(e){
20342         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20343             if(this.menu && this.menu.isVisible()){
20344                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20345             }
20346             return true;
20347         }
20348         return false;
20349     },
20350
20351     // private
20352     deactivate : function(){
20353         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20354         this.hideMenu();
20355     },
20356
20357     // private
20358     expandMenu : function(autoActivate){
20359         if(!this.disabled && this.menu){
20360             clearTimeout(this.hideTimer);
20361             delete this.hideTimer;
20362             if(!this.menu.isVisible() && !this.showTimer){
20363                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20364             }else if (this.menu.isVisible() && autoActivate){
20365                 this.menu.tryActivate(0, 1);
20366             }
20367         }
20368     },
20369
20370     // private
20371     deferExpand : function(autoActivate){
20372         delete this.showTimer;
20373         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20374         if(autoActivate){
20375             this.menu.tryActivate(0, 1);
20376         }
20377     },
20378
20379     // private
20380     hideMenu : function(){
20381         clearTimeout(this.showTimer);
20382         delete this.showTimer;
20383         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20384             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20385         }
20386     },
20387
20388     // private
20389     deferHide : function(){
20390         delete this.hideTimer;
20391         this.menu.hide();
20392     }
20393 });/*
20394  * Based on:
20395  * Ext JS Library 1.1.1
20396  * Copyright(c) 2006-2007, Ext JS, LLC.
20397  *
20398  * Originally Released Under LGPL - original licence link has changed is not relivant.
20399  *
20400  * Fork - LGPL
20401  * <script type="text/javascript">
20402  */
20403  
20404 /**
20405  * @class Roo.menu.CheckItem
20406  * @extends Roo.menu.Item
20407  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20408  * @constructor
20409  * Creates a new CheckItem
20410  * @param {Object} config Configuration options
20411  */
20412 Roo.menu.CheckItem = function(config){
20413     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20414     this.addEvents({
20415         /**
20416          * @event beforecheckchange
20417          * Fires before the checked value is set, providing an opportunity to cancel if needed
20418          * @param {Roo.menu.CheckItem} this
20419          * @param {Boolean} checked The new checked value that will be set
20420          */
20421         "beforecheckchange" : true,
20422         /**
20423          * @event checkchange
20424          * Fires after the checked value has been set
20425          * @param {Roo.menu.CheckItem} this
20426          * @param {Boolean} checked The checked value that was set
20427          */
20428         "checkchange" : true
20429     });
20430     if(this.checkHandler){
20431         this.on('checkchange', this.checkHandler, this.scope);
20432     }
20433 };
20434 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20435     /**
20436      * @cfg {String} group
20437      * All check items with the same group name will automatically be grouped into a single-select
20438      * radio button group (defaults to '')
20439      */
20440     /**
20441      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20442      */
20443     itemCls : "x-menu-item x-menu-check-item",
20444     /**
20445      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20446      */
20447     groupClass : "x-menu-group-item",
20448
20449     /**
20450      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20451      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20452      * initialized with checked = true will be rendered as checked.
20453      */
20454     checked: false,
20455
20456     // private
20457     ctype: "Roo.menu.CheckItem",
20458
20459     // private
20460     onRender : function(c){
20461         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20462         if(this.group){
20463             this.el.addClass(this.groupClass);
20464         }
20465         Roo.menu.MenuMgr.registerCheckable(this);
20466         if(this.checked){
20467             this.checked = false;
20468             this.setChecked(true, true);
20469         }
20470     },
20471
20472     // private
20473     destroy : function(){
20474         if(this.rendered){
20475             Roo.menu.MenuMgr.unregisterCheckable(this);
20476         }
20477         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20478     },
20479
20480     /**
20481      * Set the checked state of this item
20482      * @param {Boolean} checked The new checked value
20483      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20484      */
20485     setChecked : function(state, suppressEvent){
20486         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20487             if(this.container){
20488                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20489             }
20490             this.checked = state;
20491             if(suppressEvent !== true){
20492                 this.fireEvent("checkchange", this, state);
20493             }
20494         }
20495     },
20496
20497     // private
20498     handleClick : function(e){
20499        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20500            this.setChecked(!this.checked);
20501        }
20502        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20503     }
20504 });/*
20505  * Based on:
20506  * Ext JS Library 1.1.1
20507  * Copyright(c) 2006-2007, Ext JS, LLC.
20508  *
20509  * Originally Released Under LGPL - original licence link has changed is not relivant.
20510  *
20511  * Fork - LGPL
20512  * <script type="text/javascript">
20513  */
20514  
20515 /**
20516  * @class Roo.menu.DateItem
20517  * @extends Roo.menu.Adapter
20518  * A menu item that wraps the {@link Roo.DatPicker} component.
20519  * @constructor
20520  * Creates a new DateItem
20521  * @param {Object} config Configuration options
20522  */
20523 Roo.menu.DateItem = function(config){
20524     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20525     /** The Roo.DatePicker object @type Roo.DatePicker */
20526     this.picker = this.component;
20527     this.addEvents({select: true});
20528     
20529     this.picker.on("render", function(picker){
20530         picker.getEl().swallowEvent("click");
20531         picker.container.addClass("x-menu-date-item");
20532     });
20533
20534     this.picker.on("select", this.onSelect, this);
20535 };
20536
20537 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20538     // private
20539     onSelect : function(picker, date){
20540         this.fireEvent("select", this, date, picker);
20541         Roo.menu.DateItem.superclass.handleClick.call(this);
20542     }
20543 });/*
20544  * Based on:
20545  * Ext JS Library 1.1.1
20546  * Copyright(c) 2006-2007, Ext JS, LLC.
20547  *
20548  * Originally Released Under LGPL - original licence link has changed is not relivant.
20549  *
20550  * Fork - LGPL
20551  * <script type="text/javascript">
20552  */
20553  
20554 /**
20555  * @class Roo.menu.ColorItem
20556  * @extends Roo.menu.Adapter
20557  * A menu item that wraps the {@link Roo.ColorPalette} component.
20558  * @constructor
20559  * Creates a new ColorItem
20560  * @param {Object} config Configuration options
20561  */
20562 Roo.menu.ColorItem = function(config){
20563     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20564     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20565     this.palette = this.component;
20566     this.relayEvents(this.palette, ["select"]);
20567     if(this.selectHandler){
20568         this.on('select', this.selectHandler, this.scope);
20569     }
20570 };
20571 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20572  * Based on:
20573  * Ext JS Library 1.1.1
20574  * Copyright(c) 2006-2007, Ext JS, LLC.
20575  *
20576  * Originally Released Under LGPL - original licence link has changed is not relivant.
20577  *
20578  * Fork - LGPL
20579  * <script type="text/javascript">
20580  */
20581  
20582
20583 /**
20584  * @class Roo.menu.DateMenu
20585  * @extends Roo.menu.Menu
20586  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20587  * @constructor
20588  * Creates a new DateMenu
20589  * @param {Object} config Configuration options
20590  */
20591 Roo.menu.DateMenu = function(config){
20592     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20593     this.plain = true;
20594     var di = new Roo.menu.DateItem(config);
20595     this.add(di);
20596     /**
20597      * The {@link Roo.DatePicker} instance for this DateMenu
20598      * @type DatePicker
20599      */
20600     this.picker = di.picker;
20601     /**
20602      * @event select
20603      * @param {DatePicker} picker
20604      * @param {Date} date
20605      */
20606     this.relayEvents(di, ["select"]);
20607
20608     this.on('beforeshow', function(){
20609         if(this.picker){
20610             this.picker.hideMonthPicker(true);
20611         }
20612     }, this);
20613 };
20614 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20615     cls:'x-date-menu'
20616 });/*
20617  * Based on:
20618  * Ext JS Library 1.1.1
20619  * Copyright(c) 2006-2007, Ext JS, LLC.
20620  *
20621  * Originally Released Under LGPL - original licence link has changed is not relivant.
20622  *
20623  * Fork - LGPL
20624  * <script type="text/javascript">
20625  */
20626  
20627
20628 /**
20629  * @class Roo.menu.ColorMenu
20630  * @extends Roo.menu.Menu
20631  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20632  * @constructor
20633  * Creates a new ColorMenu
20634  * @param {Object} config Configuration options
20635  */
20636 Roo.menu.ColorMenu = function(config){
20637     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20638     this.plain = true;
20639     var ci = new Roo.menu.ColorItem(config);
20640     this.add(ci);
20641     /**
20642      * The {@link Roo.ColorPalette} instance for this ColorMenu
20643      * @type ColorPalette
20644      */
20645     this.palette = ci.palette;
20646     /**
20647      * @event select
20648      * @param {ColorPalette} palette
20649      * @param {String} color
20650      */
20651     this.relayEvents(ci, ["select"]);
20652 };
20653 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20654  * Based on:
20655  * Ext JS Library 1.1.1
20656  * Copyright(c) 2006-2007, Ext JS, LLC.
20657  *
20658  * Originally Released Under LGPL - original licence link has changed is not relivant.
20659  *
20660  * Fork - LGPL
20661  * <script type="text/javascript">
20662  */
20663  
20664 /**
20665  * @class Roo.form.Field
20666  * @extends Roo.BoxComponent
20667  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20668  * @constructor
20669  * Creates a new Field
20670  * @param {Object} config Configuration options
20671  */
20672 Roo.form.Field = function(config){
20673     Roo.form.Field.superclass.constructor.call(this, config);
20674 };
20675
20676 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20677     /**
20678      * @cfg {String} fieldLabel Label to use when rendering a form.
20679      */
20680        /**
20681      * @cfg {String} qtip Mouse over tip
20682      */
20683      
20684     /**
20685      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20686      */
20687     invalidClass : "x-form-invalid",
20688     /**
20689      * @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")
20690      */
20691     invalidText : "The value in this field is invalid",
20692     /**
20693      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20694      */
20695     focusClass : "x-form-focus",
20696     /**
20697      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20698       automatic validation (defaults to "keyup").
20699      */
20700     validationEvent : "keyup",
20701     /**
20702      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20703      */
20704     validateOnBlur : true,
20705     /**
20706      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20707      */
20708     validationDelay : 250,
20709     /**
20710      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20711      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20712      */
20713     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20714     /**
20715      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20716      */
20717     fieldClass : "x-form-field",
20718     /**
20719      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20720      *<pre>
20721 Value         Description
20722 -----------   ----------------------------------------------------------------------
20723 qtip          Display a quick tip when the user hovers over the field
20724 title         Display a default browser title attribute popup
20725 under         Add a block div beneath the field containing the error text
20726 side          Add an error icon to the right of the field with a popup on hover
20727 [element id]  Add the error text directly to the innerHTML of the specified element
20728 </pre>
20729      */
20730     msgTarget : 'qtip',
20731     /**
20732      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20733      */
20734     msgFx : 'normal',
20735
20736     /**
20737      * @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.
20738      */
20739     readOnly : false,
20740
20741     /**
20742      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20743      */
20744     disabled : false,
20745
20746     /**
20747      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20748      */
20749     inputType : undefined,
20750     
20751     /**
20752      * @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).
20753          */
20754         tabIndex : undefined,
20755         
20756     // private
20757     isFormField : true,
20758
20759     // private
20760     hasFocus : false,
20761     /**
20762      * @property {Roo.Element} fieldEl
20763      * Element Containing the rendered Field (with label etc.)
20764      */
20765     /**
20766      * @cfg {Mixed} value A value to initialize this field with.
20767      */
20768     value : undefined,
20769
20770     /**
20771      * @cfg {String} name The field's HTML name attribute.
20772      */
20773     /**
20774      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20775      */
20776
20777         // private ??
20778         initComponent : function(){
20779         Roo.form.Field.superclass.initComponent.call(this);
20780         this.addEvents({
20781             /**
20782              * @event focus
20783              * Fires when this field receives input focus.
20784              * @param {Roo.form.Field} this
20785              */
20786             focus : true,
20787             /**
20788              * @event blur
20789              * Fires when this field loses input focus.
20790              * @param {Roo.form.Field} this
20791              */
20792             blur : true,
20793             /**
20794              * @event specialkey
20795              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20796              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20797              * @param {Roo.form.Field} this
20798              * @param {Roo.EventObject} e The event object
20799              */
20800             specialkey : true,
20801             /**
20802              * @event change
20803              * Fires just before the field blurs if the field value has changed.
20804              * @param {Roo.form.Field} this
20805              * @param {Mixed} newValue The new value
20806              * @param {Mixed} oldValue The original value
20807              */
20808             change : true,
20809             /**
20810              * @event invalid
20811              * Fires after the field has been marked as invalid.
20812              * @param {Roo.form.Field} this
20813              * @param {String} msg The validation message
20814              */
20815             invalid : true,
20816             /**
20817              * @event valid
20818              * Fires after the field has been validated with no errors.
20819              * @param {Roo.form.Field} this
20820              */
20821             valid : true
20822         });
20823     },
20824
20825     /**
20826      * Returns the name attribute of the field if available
20827      * @return {String} name The field name
20828      */
20829     getName: function(){
20830          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20831     },
20832
20833     // private
20834     onRender : function(ct, position){
20835         Roo.form.Field.superclass.onRender.call(this, ct, position);
20836         if(!this.el){
20837             var cfg = this.getAutoCreate();
20838             if(!cfg.name){
20839                 cfg.name = this.name || this.id;
20840             }
20841             if(this.inputType){
20842                 cfg.type = this.inputType;
20843             }
20844             this.el = ct.createChild(cfg, position);
20845         }
20846         var type = this.el.dom.type;
20847         if(type){
20848             if(type == 'password'){
20849                 type = 'text';
20850             }
20851             this.el.addClass('x-form-'+type);
20852         }
20853         if(this.readOnly){
20854             this.el.dom.readOnly = true;
20855         }
20856         if(this.tabIndex !== undefined){
20857             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20858         }
20859
20860         this.el.addClass([this.fieldClass, this.cls]);
20861         this.initValue();
20862     },
20863
20864     /**
20865      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20866      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20867      * @return {Roo.form.Field} this
20868      */
20869     applyTo : function(target){
20870         this.allowDomMove = false;
20871         this.el = Roo.get(target);
20872         this.render(this.el.dom.parentNode);
20873         return this;
20874     },
20875
20876     // private
20877     initValue : function(){
20878         if(this.value !== undefined){
20879             this.setValue(this.value);
20880         }else if(this.el.dom.value.length > 0){
20881             this.setValue(this.el.dom.value);
20882         }
20883     },
20884
20885     /**
20886      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20887      */
20888     isDirty : function() {
20889         if(this.disabled) {
20890             return false;
20891         }
20892         return String(this.getValue()) !== String(this.originalValue);
20893     },
20894
20895     // private
20896     afterRender : function(){
20897         Roo.form.Field.superclass.afterRender.call(this);
20898         this.initEvents();
20899     },
20900
20901     // private
20902     fireKey : function(e){
20903         //Roo.log('field ' + e.getKey());
20904         if(e.isNavKeyPress()){
20905             this.fireEvent("specialkey", this, e);
20906         }
20907     },
20908
20909     /**
20910      * Resets the current field value to the originally loaded value and clears any validation messages
20911      */
20912     reset : function(){
20913         this.setValue(this.originalValue);
20914         this.clearInvalid();
20915     },
20916
20917     // private
20918     initEvents : function(){
20919         // safari killled keypress - so keydown is now used..
20920         this.el.on("keydown" , this.fireKey,  this);
20921         this.el.on("focus", this.onFocus,  this);
20922         this.el.on("blur", this.onBlur,  this);
20923
20924         // reference to original value for reset
20925         this.originalValue = this.getValue();
20926     },
20927
20928     // private
20929     onFocus : function(){
20930         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20931             this.el.addClass(this.focusClass);
20932         }
20933         if(!this.hasFocus){
20934             this.hasFocus = true;
20935             this.startValue = this.getValue();
20936             this.fireEvent("focus", this);
20937         }
20938     },
20939
20940     beforeBlur : Roo.emptyFn,
20941
20942     // private
20943     onBlur : function(){
20944         this.beforeBlur();
20945         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20946             this.el.removeClass(this.focusClass);
20947         }
20948         this.hasFocus = false;
20949         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20950             this.validate();
20951         }
20952         var v = this.getValue();
20953         if(String(v) !== String(this.startValue)){
20954             this.fireEvent('change', this, v, this.startValue);
20955         }
20956         this.fireEvent("blur", this);
20957     },
20958
20959     /**
20960      * Returns whether or not the field value is currently valid
20961      * @param {Boolean} preventMark True to disable marking the field invalid
20962      * @return {Boolean} True if the value is valid, else false
20963      */
20964     isValid : function(preventMark){
20965         if(this.disabled){
20966             return true;
20967         }
20968         var restore = this.preventMark;
20969         this.preventMark = preventMark === true;
20970         var v = this.validateValue(this.processValue(this.getRawValue()));
20971         this.preventMark = restore;
20972         return v;
20973     },
20974
20975     /**
20976      * Validates the field value
20977      * @return {Boolean} True if the value is valid, else false
20978      */
20979     validate : function(){
20980         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
20981             this.clearInvalid();
20982             return true;
20983         }
20984         return false;
20985     },
20986
20987     processValue : function(value){
20988         return value;
20989     },
20990
20991     // private
20992     // Subclasses should provide the validation implementation by overriding this
20993     validateValue : function(value){
20994         return true;
20995     },
20996
20997     /**
20998      * Mark this field as invalid
20999      * @param {String} msg The validation message
21000      */
21001     markInvalid : function(msg){
21002         if(!this.rendered || this.preventMark){ // not rendered
21003             return;
21004         }
21005         this.el.addClass(this.invalidClass);
21006         msg = msg || this.invalidText;
21007         switch(this.msgTarget){
21008             case 'qtip':
21009                 this.el.dom.qtip = msg;
21010                 this.el.dom.qclass = 'x-form-invalid-tip';
21011                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21012                     Roo.QuickTips.enable();
21013                 }
21014                 break;
21015             case 'title':
21016                 this.el.dom.title = msg;
21017                 break;
21018             case 'under':
21019                 if(!this.errorEl){
21020                     var elp = this.el.findParent('.x-form-element', 5, true);
21021                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21022                     this.errorEl.setWidth(elp.getWidth(true)-20);
21023                 }
21024                 this.errorEl.update(msg);
21025                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21026                 break;
21027             case 'side':
21028                 if(!this.errorIcon){
21029                     var elp = this.el.findParent('.x-form-element', 5, true);
21030                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21031                 }
21032                 this.alignErrorIcon();
21033                 this.errorIcon.dom.qtip = msg;
21034                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21035                 this.errorIcon.show();
21036                 this.on('resize', this.alignErrorIcon, this);
21037                 break;
21038             default:
21039                 var t = Roo.getDom(this.msgTarget);
21040                 t.innerHTML = msg;
21041                 t.style.display = this.msgDisplay;
21042                 break;
21043         }
21044         this.fireEvent('invalid', this, msg);
21045     },
21046
21047     // private
21048     alignErrorIcon : function(){
21049         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21050     },
21051
21052     /**
21053      * Clear any invalid styles/messages for this field
21054      */
21055     clearInvalid : function(){
21056         if(!this.rendered || this.preventMark){ // not rendered
21057             return;
21058         }
21059         this.el.removeClass(this.invalidClass);
21060         switch(this.msgTarget){
21061             case 'qtip':
21062                 this.el.dom.qtip = '';
21063                 break;
21064             case 'title':
21065                 this.el.dom.title = '';
21066                 break;
21067             case 'under':
21068                 if(this.errorEl){
21069                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21070                 }
21071                 break;
21072             case 'side':
21073                 if(this.errorIcon){
21074                     this.errorIcon.dom.qtip = '';
21075                     this.errorIcon.hide();
21076                     this.un('resize', this.alignErrorIcon, this);
21077                 }
21078                 break;
21079             default:
21080                 var t = Roo.getDom(this.msgTarget);
21081                 t.innerHTML = '';
21082                 t.style.display = 'none';
21083                 break;
21084         }
21085         this.fireEvent('valid', this);
21086     },
21087
21088     /**
21089      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21090      * @return {Mixed} value The field value
21091      */
21092     getRawValue : function(){
21093         var v = this.el.getValue();
21094         if(v === this.emptyText){
21095             v = '';
21096         }
21097         return v;
21098     },
21099
21100     /**
21101      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21102      * @return {Mixed} value The field value
21103      */
21104     getValue : function(){
21105         var v = this.el.getValue();
21106         if(v === this.emptyText || v === undefined){
21107             v = '';
21108         }
21109         return v;
21110     },
21111
21112     /**
21113      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21114      * @param {Mixed} value The value to set
21115      */
21116     setRawValue : function(v){
21117         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21118     },
21119
21120     /**
21121      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21122      * @param {Mixed} value The value to set
21123      */
21124     setValue : function(v){
21125         this.value = v;
21126         if(this.rendered){
21127             this.el.dom.value = (v === null || v === undefined ? '' : v);
21128             this.validate();
21129         }
21130     },
21131
21132     adjustSize : function(w, h){
21133         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21134         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21135         return s;
21136     },
21137
21138     adjustWidth : function(tag, w){
21139         tag = tag.toLowerCase();
21140         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21141             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21142                 if(tag == 'input'){
21143                     return w + 2;
21144                 }
21145                 if(tag = 'textarea'){
21146                     return w-2;
21147                 }
21148             }else if(Roo.isOpera){
21149                 if(tag == 'input'){
21150                     return w + 2;
21151                 }
21152                 if(tag = 'textarea'){
21153                     return w-2;
21154                 }
21155             }
21156         }
21157         return w;
21158     }
21159 });
21160
21161
21162 // anything other than normal should be considered experimental
21163 Roo.form.Field.msgFx = {
21164     normal : {
21165         show: function(msgEl, f){
21166             msgEl.setDisplayed('block');
21167         },
21168
21169         hide : function(msgEl, f){
21170             msgEl.setDisplayed(false).update('');
21171         }
21172     },
21173
21174     slide : {
21175         show: function(msgEl, f){
21176             msgEl.slideIn('t', {stopFx:true});
21177         },
21178
21179         hide : function(msgEl, f){
21180             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21181         }
21182     },
21183
21184     slideRight : {
21185         show: function(msgEl, f){
21186             msgEl.fixDisplay();
21187             msgEl.alignTo(f.el, 'tl-tr');
21188             msgEl.slideIn('l', {stopFx:true});
21189         },
21190
21191         hide : function(msgEl, f){
21192             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21193         }
21194     }
21195 };/*
21196  * Based on:
21197  * Ext JS Library 1.1.1
21198  * Copyright(c) 2006-2007, Ext JS, LLC.
21199  *
21200  * Originally Released Under LGPL - original licence link has changed is not relivant.
21201  *
21202  * Fork - LGPL
21203  * <script type="text/javascript">
21204  */
21205  
21206
21207 /**
21208  * @class Roo.form.TextField
21209  * @extends Roo.form.Field
21210  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21211  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21212  * @constructor
21213  * Creates a new TextField
21214  * @param {Object} config Configuration options
21215  */
21216 Roo.form.TextField = function(config){
21217     Roo.form.TextField.superclass.constructor.call(this, config);
21218     this.addEvents({
21219         /**
21220          * @event autosize
21221          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21222          * according to the default logic, but this event provides a hook for the developer to apply additional
21223          * logic at runtime to resize the field if needed.
21224              * @param {Roo.form.Field} this This text field
21225              * @param {Number} width The new field width
21226              */
21227         autosize : true
21228     });
21229 };
21230
21231 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21232     /**
21233      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21234      */
21235     grow : false,
21236     /**
21237      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21238      */
21239     growMin : 30,
21240     /**
21241      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21242      */
21243     growMax : 800,
21244     /**
21245      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21246      */
21247     vtype : null,
21248     /**
21249      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21250      */
21251     maskRe : null,
21252     /**
21253      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21254      */
21255     disableKeyFilter : false,
21256     /**
21257      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21258      */
21259     allowBlank : true,
21260     /**
21261      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21262      */
21263     minLength : 0,
21264     /**
21265      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21266      */
21267     maxLength : Number.MAX_VALUE,
21268     /**
21269      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21270      */
21271     minLengthText : "The minimum length for this field is {0}",
21272     /**
21273      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21274      */
21275     maxLengthText : "The maximum length for this field is {0}",
21276     /**
21277      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21278      */
21279     selectOnFocus : false,
21280     /**
21281      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21282      */
21283     blankText : "This field is required",
21284     /**
21285      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21286      * If available, this function will be called only after the basic validators all return true, and will be passed the
21287      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21288      */
21289     validator : null,
21290     /**
21291      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21292      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21293      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21294      */
21295     regex : null,
21296     /**
21297      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21298      */
21299     regexText : "",
21300     /**
21301      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21302      */
21303     emptyText : null,
21304     /**
21305      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21306      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21307      */
21308     emptyClass : 'x-form-empty-field',
21309
21310     // private
21311     initEvents : function(){
21312         Roo.form.TextField.superclass.initEvents.call(this);
21313         if(this.validationEvent == 'keyup'){
21314             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21315             this.el.on('keyup', this.filterValidation, this);
21316         }
21317         else if(this.validationEvent !== false){
21318             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21319         }
21320         if(this.selectOnFocus || this.emptyText){
21321             this.on("focus", this.preFocus, this);
21322             if(this.emptyText){
21323                 this.on('blur', this.postBlur, this);
21324                 this.applyEmptyText();
21325             }
21326         }
21327         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21328             this.el.on("keypress", this.filterKeys, this);
21329         }
21330         if(this.grow){
21331             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21332             this.el.on("click", this.autoSize,  this);
21333         }
21334     },
21335
21336     processValue : function(value){
21337         if(this.stripCharsRe){
21338             var newValue = value.replace(this.stripCharsRe, '');
21339             if(newValue !== value){
21340                 this.setRawValue(newValue);
21341                 return newValue;
21342             }
21343         }
21344         return value;
21345     },
21346
21347     filterValidation : function(e){
21348         if(!e.isNavKeyPress()){
21349             this.validationTask.delay(this.validationDelay);
21350         }
21351     },
21352
21353     // private
21354     onKeyUp : function(e){
21355         if(!e.isNavKeyPress()){
21356             this.autoSize();
21357         }
21358     },
21359
21360     /**
21361      * Resets the current field value to the originally-loaded value and clears any validation messages.
21362      * Also adds emptyText and emptyClass if the original value was blank.
21363      */
21364     reset : function(){
21365         Roo.form.TextField.superclass.reset.call(this);
21366         this.applyEmptyText();
21367     },
21368
21369     applyEmptyText : function(){
21370         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21371             this.setRawValue(this.emptyText);
21372             this.el.addClass(this.emptyClass);
21373         }
21374     },
21375
21376     // private
21377     preFocus : function(){
21378         if(this.emptyText){
21379             if(this.el.dom.value == this.emptyText){
21380                 this.setRawValue('');
21381             }
21382             this.el.removeClass(this.emptyClass);
21383         }
21384         if(this.selectOnFocus){
21385             this.el.dom.select();
21386         }
21387     },
21388
21389     // private
21390     postBlur : function(){
21391         this.applyEmptyText();
21392     },
21393
21394     // private
21395     filterKeys : function(e){
21396         var k = e.getKey();
21397         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21398             return;
21399         }
21400         var c = e.getCharCode(), cc = String.fromCharCode(c);
21401         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21402             return;
21403         }
21404         if(!this.maskRe.test(cc)){
21405             e.stopEvent();
21406         }
21407     },
21408
21409     setValue : function(v){
21410         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21411             this.el.removeClass(this.emptyClass);
21412         }
21413         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21414         this.applyEmptyText();
21415         this.autoSize();
21416     },
21417
21418     /**
21419      * Validates a value according to the field's validation rules and marks the field as invalid
21420      * if the validation fails
21421      * @param {Mixed} value The value to validate
21422      * @return {Boolean} True if the value is valid, else false
21423      */
21424     validateValue : function(value){
21425         if(value.length < 1 || value === this.emptyText){ // if it's blank
21426              if(this.allowBlank){
21427                 this.clearInvalid();
21428                 return true;
21429              }else{
21430                 this.markInvalid(this.blankText);
21431                 return false;
21432              }
21433         }
21434         if(value.length < this.minLength){
21435             this.markInvalid(String.format(this.minLengthText, this.minLength));
21436             return false;
21437         }
21438         if(value.length > this.maxLength){
21439             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21440             return false;
21441         }
21442         if(this.vtype){
21443             var vt = Roo.form.VTypes;
21444             if(!vt[this.vtype](value, this)){
21445                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21446                 return false;
21447             }
21448         }
21449         if(typeof this.validator == "function"){
21450             var msg = this.validator(value);
21451             if(msg !== true){
21452                 this.markInvalid(msg);
21453                 return false;
21454             }
21455         }
21456         if(this.regex && !this.regex.test(value)){
21457             this.markInvalid(this.regexText);
21458             return false;
21459         }
21460         return true;
21461     },
21462
21463     /**
21464      * Selects text in this field
21465      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21466      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21467      */
21468     selectText : function(start, end){
21469         var v = this.getRawValue();
21470         if(v.length > 0){
21471             start = start === undefined ? 0 : start;
21472             end = end === undefined ? v.length : end;
21473             var d = this.el.dom;
21474             if(d.setSelectionRange){
21475                 d.setSelectionRange(start, end);
21476             }else if(d.createTextRange){
21477                 var range = d.createTextRange();
21478                 range.moveStart("character", start);
21479                 range.moveEnd("character", v.length-end);
21480                 range.select();
21481             }
21482         }
21483     },
21484
21485     /**
21486      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21487      * This only takes effect if grow = true, and fires the autosize event.
21488      */
21489     autoSize : function(){
21490         if(!this.grow || !this.rendered){
21491             return;
21492         }
21493         if(!this.metrics){
21494             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21495         }
21496         var el = this.el;
21497         var v = el.dom.value;
21498         var d = document.createElement('div');
21499         d.appendChild(document.createTextNode(v));
21500         v = d.innerHTML;
21501         d = null;
21502         v += "&#160;";
21503         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21504         this.el.setWidth(w);
21505         this.fireEvent("autosize", this, w);
21506     }
21507 });/*
21508  * Based on:
21509  * Ext JS Library 1.1.1
21510  * Copyright(c) 2006-2007, Ext JS, LLC.
21511  *
21512  * Originally Released Under LGPL - original licence link has changed is not relivant.
21513  *
21514  * Fork - LGPL
21515  * <script type="text/javascript">
21516  */
21517  
21518 /**
21519  * @class Roo.form.Hidden
21520  * @extends Roo.form.TextField
21521  * Simple Hidden element used on forms 
21522  * 
21523  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21524  * 
21525  * @constructor
21526  * Creates a new Hidden form element.
21527  * @param {Object} config Configuration options
21528  */
21529
21530
21531
21532 // easy hidden field...
21533 Roo.form.Hidden = function(config){
21534     Roo.form.Hidden.superclass.constructor.call(this, config);
21535 };
21536   
21537 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21538     fieldLabel:      '',
21539     inputType:      'hidden',
21540     width:          50,
21541     allowBlank:     true,
21542     labelSeparator: '',
21543     hidden:         true,
21544     itemCls :       'x-form-item-display-none'
21545
21546
21547 });
21548
21549
21550 /*
21551  * Based on:
21552  * Ext JS Library 1.1.1
21553  * Copyright(c) 2006-2007, Ext JS, LLC.
21554  *
21555  * Originally Released Under LGPL - original licence link has changed is not relivant.
21556  *
21557  * Fork - LGPL
21558  * <script type="text/javascript">
21559  */
21560  
21561 /**
21562  * @class Roo.form.TriggerField
21563  * @extends Roo.form.TextField
21564  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21565  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21566  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21567  * for which you can provide a custom implementation.  For example:
21568  * <pre><code>
21569 var trigger = new Roo.form.TriggerField();
21570 trigger.onTriggerClick = myTriggerFn;
21571 trigger.applyTo('my-field');
21572 </code></pre>
21573  *
21574  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21575  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21576  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21577  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21578  * @constructor
21579  * Create a new TriggerField.
21580  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21581  * to the base TextField)
21582  */
21583 Roo.form.TriggerField = function(config){
21584     this.mimicing = false;
21585     Roo.form.TriggerField.superclass.constructor.call(this, config);
21586 };
21587
21588 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21589     /**
21590      * @cfg {String} triggerClass A CSS class to apply to the trigger
21591      */
21592     /**
21593      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21594      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21595      */
21596     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21597     /**
21598      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21599      */
21600     hideTrigger:false,
21601
21602     /** @cfg {Boolean} grow @hide */
21603     /** @cfg {Number} growMin @hide */
21604     /** @cfg {Number} growMax @hide */
21605
21606     /**
21607      * @hide 
21608      * @method
21609      */
21610     autoSize: Roo.emptyFn,
21611     // private
21612     monitorTab : true,
21613     // private
21614     deferHeight : true,
21615
21616     
21617     actionMode : 'wrap',
21618     // private
21619     onResize : function(w, h){
21620         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21621         if(typeof w == 'number'){
21622             var x = w - this.trigger.getWidth();
21623             this.el.setWidth(this.adjustWidth('input', x));
21624             this.trigger.setStyle('left', x+'px');
21625         }
21626     },
21627
21628     // private
21629     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21630
21631     // private
21632     getResizeEl : function(){
21633         return this.wrap;
21634     },
21635
21636     // private
21637     getPositionEl : function(){
21638         return this.wrap;
21639     },
21640
21641     // private
21642     alignErrorIcon : function(){
21643         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21644     },
21645
21646     // private
21647     onRender : function(ct, position){
21648         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21649         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21650         this.trigger = this.wrap.createChild(this.triggerConfig ||
21651                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21652         if(this.hideTrigger){
21653             this.trigger.setDisplayed(false);
21654         }
21655         this.initTrigger();
21656         if(!this.width){
21657             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21658         }
21659     },
21660
21661     // private
21662     initTrigger : function(){
21663         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21664         this.trigger.addClassOnOver('x-form-trigger-over');
21665         this.trigger.addClassOnClick('x-form-trigger-click');
21666     },
21667
21668     // private
21669     onDestroy : function(){
21670         if(this.trigger){
21671             this.trigger.removeAllListeners();
21672             this.trigger.remove();
21673         }
21674         if(this.wrap){
21675             this.wrap.remove();
21676         }
21677         Roo.form.TriggerField.superclass.onDestroy.call(this);
21678     },
21679
21680     // private
21681     onFocus : function(){
21682         Roo.form.TriggerField.superclass.onFocus.call(this);
21683         if(!this.mimicing){
21684             this.wrap.addClass('x-trigger-wrap-focus');
21685             this.mimicing = true;
21686             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21687             if(this.monitorTab){
21688                 this.el.on("keydown", this.checkTab, this);
21689             }
21690         }
21691     },
21692
21693     // private
21694     checkTab : function(e){
21695         if(e.getKey() == e.TAB){
21696             this.triggerBlur();
21697         }
21698     },
21699
21700     // private
21701     onBlur : function(){
21702         // do nothing
21703     },
21704
21705     // private
21706     mimicBlur : function(e, t){
21707         if(!this.wrap.contains(t) && this.validateBlur()){
21708             this.triggerBlur();
21709         }
21710     },
21711
21712     // private
21713     triggerBlur : function(){
21714         this.mimicing = false;
21715         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21716         if(this.monitorTab){
21717             this.el.un("keydown", this.checkTab, this);
21718         }
21719         this.wrap.removeClass('x-trigger-wrap-focus');
21720         Roo.form.TriggerField.superclass.onBlur.call(this);
21721     },
21722
21723     // private
21724     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21725     validateBlur : function(e, t){
21726         return true;
21727     },
21728
21729     // private
21730     onDisable : function(){
21731         Roo.form.TriggerField.superclass.onDisable.call(this);
21732         if(this.wrap){
21733             this.wrap.addClass('x-item-disabled');
21734         }
21735     },
21736
21737     // private
21738     onEnable : function(){
21739         Roo.form.TriggerField.superclass.onEnable.call(this);
21740         if(this.wrap){
21741             this.wrap.removeClass('x-item-disabled');
21742         }
21743     },
21744
21745     // private
21746     onShow : function(){
21747         var ae = this.getActionEl();
21748         
21749         if(ae){
21750             ae.dom.style.display = '';
21751             ae.dom.style.visibility = 'visible';
21752         }
21753     },
21754
21755     // private
21756     
21757     onHide : function(){
21758         var ae = this.getActionEl();
21759         ae.dom.style.display = 'none';
21760     },
21761
21762     /**
21763      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21764      * by an implementing function.
21765      * @method
21766      * @param {EventObject} e
21767      */
21768     onTriggerClick : Roo.emptyFn
21769 });
21770
21771 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21772 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21773 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21774 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21775     initComponent : function(){
21776         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21777
21778         this.triggerConfig = {
21779             tag:'span', cls:'x-form-twin-triggers', cn:[
21780             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21781             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21782         ]};
21783     },
21784
21785     getTrigger : function(index){
21786         return this.triggers[index];
21787     },
21788
21789     initTrigger : function(){
21790         var ts = this.trigger.select('.x-form-trigger', true);
21791         this.wrap.setStyle('overflow', 'hidden');
21792         var triggerField = this;
21793         ts.each(function(t, all, index){
21794             t.hide = function(){
21795                 var w = triggerField.wrap.getWidth();
21796                 this.dom.style.display = 'none';
21797                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21798             };
21799             t.show = function(){
21800                 var w = triggerField.wrap.getWidth();
21801                 this.dom.style.display = '';
21802                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21803             };
21804             var triggerIndex = 'Trigger'+(index+1);
21805
21806             if(this['hide'+triggerIndex]){
21807                 t.dom.style.display = 'none';
21808             }
21809             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21810             t.addClassOnOver('x-form-trigger-over');
21811             t.addClassOnClick('x-form-trigger-click');
21812         }, this);
21813         this.triggers = ts.elements;
21814     },
21815
21816     onTrigger1Click : Roo.emptyFn,
21817     onTrigger2Click : Roo.emptyFn
21818 });/*
21819  * Based on:
21820  * Ext JS Library 1.1.1
21821  * Copyright(c) 2006-2007, Ext JS, LLC.
21822  *
21823  * Originally Released Under LGPL - original licence link has changed is not relivant.
21824  *
21825  * Fork - LGPL
21826  * <script type="text/javascript">
21827  */
21828  
21829 /**
21830  * @class Roo.form.TextArea
21831  * @extends Roo.form.TextField
21832  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21833  * support for auto-sizing.
21834  * @constructor
21835  * Creates a new TextArea
21836  * @param {Object} config Configuration options
21837  */
21838 Roo.form.TextArea = function(config){
21839     Roo.form.TextArea.superclass.constructor.call(this, config);
21840     // these are provided exchanges for backwards compat
21841     // minHeight/maxHeight were replaced by growMin/growMax to be
21842     // compatible with TextField growing config values
21843     if(this.minHeight !== undefined){
21844         this.growMin = this.minHeight;
21845     }
21846     if(this.maxHeight !== undefined){
21847         this.growMax = this.maxHeight;
21848     }
21849 };
21850
21851 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21852     /**
21853      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21854      */
21855     growMin : 60,
21856     /**
21857      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21858      */
21859     growMax: 1000,
21860     /**
21861      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21862      * in the field (equivalent to setting overflow: hidden, defaults to false)
21863      */
21864     preventScrollbars: false,
21865     /**
21866      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21867      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21868      */
21869
21870     // private
21871     onRender : function(ct, position){
21872         if(!this.el){
21873             this.defaultAutoCreate = {
21874                 tag: "textarea",
21875                 style:"width:300px;height:60px;",
21876                 autocomplete: "off"
21877             };
21878         }
21879         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21880         if(this.grow){
21881             this.textSizeEl = Roo.DomHelper.append(document.body, {
21882                 tag: "pre", cls: "x-form-grow-sizer"
21883             });
21884             if(this.preventScrollbars){
21885                 this.el.setStyle("overflow", "hidden");
21886             }
21887             this.el.setHeight(this.growMin);
21888         }
21889     },
21890
21891     onDestroy : function(){
21892         if(this.textSizeEl){
21893             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21894         }
21895         Roo.form.TextArea.superclass.onDestroy.call(this);
21896     },
21897
21898     // private
21899     onKeyUp : function(e){
21900         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21901             this.autoSize();
21902         }
21903     },
21904
21905     /**
21906      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21907      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21908      */
21909     autoSize : function(){
21910         if(!this.grow || !this.textSizeEl){
21911             return;
21912         }
21913         var el = this.el;
21914         var v = el.dom.value;
21915         var ts = this.textSizeEl;
21916
21917         ts.innerHTML = '';
21918         ts.appendChild(document.createTextNode(v));
21919         v = ts.innerHTML;
21920
21921         Roo.fly(ts).setWidth(this.el.getWidth());
21922         if(v.length < 1){
21923             v = "&#160;&#160;";
21924         }else{
21925             if(Roo.isIE){
21926                 v = v.replace(/\n/g, '<p>&#160;</p>');
21927             }
21928             v += "&#160;\n&#160;";
21929         }
21930         ts.innerHTML = v;
21931         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21932         if(h != this.lastHeight){
21933             this.lastHeight = h;
21934             this.el.setHeight(h);
21935             this.fireEvent("autosize", this, h);
21936         }
21937     }
21938 });/*
21939  * Based on:
21940  * Ext JS Library 1.1.1
21941  * Copyright(c) 2006-2007, Ext JS, LLC.
21942  *
21943  * Originally Released Under LGPL - original licence link has changed is not relivant.
21944  *
21945  * Fork - LGPL
21946  * <script type="text/javascript">
21947  */
21948  
21949
21950 /**
21951  * @class Roo.form.NumberField
21952  * @extends Roo.form.TextField
21953  * Numeric text field that provides automatic keystroke filtering and numeric validation.
21954  * @constructor
21955  * Creates a new NumberField
21956  * @param {Object} config Configuration options
21957  */
21958 Roo.form.NumberField = function(config){
21959     Roo.form.NumberField.superclass.constructor.call(this, config);
21960 };
21961
21962 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
21963     /**
21964      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
21965      */
21966     fieldClass: "x-form-field x-form-num-field",
21967     /**
21968      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
21969      */
21970     allowDecimals : true,
21971     /**
21972      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
21973      */
21974     decimalSeparator : ".",
21975     /**
21976      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
21977      */
21978     decimalPrecision : 2,
21979     /**
21980      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
21981      */
21982     allowNegative : true,
21983     /**
21984      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
21985      */
21986     minValue : Number.NEGATIVE_INFINITY,
21987     /**
21988      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
21989      */
21990     maxValue : Number.MAX_VALUE,
21991     /**
21992      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
21993      */
21994     minText : "The minimum value for this field is {0}",
21995     /**
21996      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
21997      */
21998     maxText : "The maximum value for this field is {0}",
21999     /**
22000      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22001      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22002      */
22003     nanText : "{0} is not a valid number",
22004
22005     // private
22006     initEvents : function(){
22007         Roo.form.NumberField.superclass.initEvents.call(this);
22008         var allowed = "0123456789";
22009         if(this.allowDecimals){
22010             allowed += this.decimalSeparator;
22011         }
22012         if(this.allowNegative){
22013             allowed += "-";
22014         }
22015         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22016         var keyPress = function(e){
22017             var k = e.getKey();
22018             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22019                 return;
22020             }
22021             var c = e.getCharCode();
22022             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22023                 e.stopEvent();
22024             }
22025         };
22026         this.el.on("keypress", keyPress, this);
22027     },
22028
22029     // private
22030     validateValue : function(value){
22031         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22032             return false;
22033         }
22034         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22035              return true;
22036         }
22037         var num = this.parseValue(value);
22038         if(isNaN(num)){
22039             this.markInvalid(String.format(this.nanText, value));
22040             return false;
22041         }
22042         if(num < this.minValue){
22043             this.markInvalid(String.format(this.minText, this.minValue));
22044             return false;
22045         }
22046         if(num > this.maxValue){
22047             this.markInvalid(String.format(this.maxText, this.maxValue));
22048             return false;
22049         }
22050         return true;
22051     },
22052
22053     getValue : function(){
22054         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22055     },
22056
22057     // private
22058     parseValue : function(value){
22059         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22060         return isNaN(value) ? '' : value;
22061     },
22062
22063     // private
22064     fixPrecision : function(value){
22065         var nan = isNaN(value);
22066         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22067             return nan ? '' : value;
22068         }
22069         return parseFloat(value).toFixed(this.decimalPrecision);
22070     },
22071
22072     setValue : function(v){
22073         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22074     },
22075
22076     // private
22077     decimalPrecisionFcn : function(v){
22078         return Math.floor(v);
22079     },
22080
22081     beforeBlur : function(){
22082         var v = this.parseValue(this.getRawValue());
22083         if(v){
22084             this.setValue(this.fixPrecision(v));
22085         }
22086     }
22087 });/*
22088  * Based on:
22089  * Ext JS Library 1.1.1
22090  * Copyright(c) 2006-2007, Ext JS, LLC.
22091  *
22092  * Originally Released Under LGPL - original licence link has changed is not relivant.
22093  *
22094  * Fork - LGPL
22095  * <script type="text/javascript">
22096  */
22097  
22098 /**
22099  * @class Roo.form.DateField
22100  * @extends Roo.form.TriggerField
22101  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22102 * @constructor
22103 * Create a new DateField
22104 * @param {Object} config
22105  */
22106 Roo.form.DateField = function(config){
22107     Roo.form.DateField.superclass.constructor.call(this, config);
22108     
22109       this.addEvents({
22110          
22111         /**
22112          * @event select
22113          * Fires when a date is selected
22114              * @param {Roo.form.DateField} combo This combo box
22115              * @param {Date} date The date selected
22116              */
22117         'select' : true
22118          
22119     });
22120     
22121     
22122     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22123     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22124     this.ddMatch = null;
22125     if(this.disabledDates){
22126         var dd = this.disabledDates;
22127         var re = "(?:";
22128         for(var i = 0; i < dd.length; i++){
22129             re += dd[i];
22130             if(i != dd.length-1) re += "|";
22131         }
22132         this.ddMatch = new RegExp(re + ")");
22133     }
22134 };
22135
22136 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22137     /**
22138      * @cfg {String} format
22139      * The default date format string which can be overriden for localization support.  The format must be
22140      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22141      */
22142     format : "m/d/y",
22143     /**
22144      * @cfg {String} altFormats
22145      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22146      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22147      */
22148     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22149     /**
22150      * @cfg {Array} disabledDays
22151      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22152      */
22153     disabledDays : null,
22154     /**
22155      * @cfg {String} disabledDaysText
22156      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22157      */
22158     disabledDaysText : "Disabled",
22159     /**
22160      * @cfg {Array} disabledDates
22161      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22162      * expression so they are very powerful. Some examples:
22163      * <ul>
22164      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22165      * <li>["03/08", "09/16"] would disable those days for every year</li>
22166      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22167      * <li>["03/../2006"] would disable every day in March 2006</li>
22168      * <li>["^03"] would disable every day in every March</li>
22169      * </ul>
22170      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22171      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22172      */
22173     disabledDates : null,
22174     /**
22175      * @cfg {String} disabledDatesText
22176      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22177      */
22178     disabledDatesText : "Disabled",
22179     /**
22180      * @cfg {Date/String} minValue
22181      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22182      * valid format (defaults to null).
22183      */
22184     minValue : null,
22185     /**
22186      * @cfg {Date/String} maxValue
22187      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22188      * valid format (defaults to null).
22189      */
22190     maxValue : null,
22191     /**
22192      * @cfg {String} minText
22193      * The error text to display when the date in the cell is before minValue (defaults to
22194      * 'The date in this field must be after {minValue}').
22195      */
22196     minText : "The date in this field must be equal to or after {0}",
22197     /**
22198      * @cfg {String} maxText
22199      * The error text to display when the date in the cell is after maxValue (defaults to
22200      * 'The date in this field must be before {maxValue}').
22201      */
22202     maxText : "The date in this field must be equal to or before {0}",
22203     /**
22204      * @cfg {String} invalidText
22205      * The error text to display when the date in the field is invalid (defaults to
22206      * '{value} is not a valid date - it must be in the format {format}').
22207      */
22208     invalidText : "{0} is not a valid date - it must be in the format {1}",
22209     /**
22210      * @cfg {String} triggerClass
22211      * An additional CSS class used to style the trigger button.  The trigger will always get the
22212      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22213      * which displays a calendar icon).
22214      */
22215     triggerClass : 'x-form-date-trigger',
22216     
22217
22218     /**
22219      * @cfg {bool} useIso
22220      * if enabled, then the date field will use a hidden field to store the 
22221      * real value as iso formated date. default (false)
22222      */ 
22223     useIso : false,
22224     /**
22225      * @cfg {String/Object} autoCreate
22226      * A DomHelper element spec, or true for a default element spec (defaults to
22227      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22228      */ 
22229     // private
22230     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22231     
22232     // private
22233     hiddenField: false,
22234     
22235     onRender : function(ct, position)
22236     {
22237         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22238         if (this.useIso) {
22239             this.el.dom.removeAttribute('name'); 
22240             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22241                     'before', true);
22242             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22243             // prevent input submission
22244             this.hiddenName = this.name;
22245         }
22246             
22247             
22248     },
22249     
22250     // private
22251     validateValue : function(value)
22252     {
22253         value = this.formatDate(value);
22254         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22255             return false;
22256         }
22257         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22258              return true;
22259         }
22260         var svalue = value;
22261         value = this.parseDate(value);
22262         if(!value){
22263             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22264             return false;
22265         }
22266         var time = value.getTime();
22267         if(this.minValue && time < this.minValue.getTime()){
22268             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22269             return false;
22270         }
22271         if(this.maxValue && time > this.maxValue.getTime()){
22272             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22273             return false;
22274         }
22275         if(this.disabledDays){
22276             var day = value.getDay();
22277             for(var i = 0; i < this.disabledDays.length; i++) {
22278                 if(day === this.disabledDays[i]){
22279                     this.markInvalid(this.disabledDaysText);
22280                     return false;
22281                 }
22282             }
22283         }
22284         var fvalue = this.formatDate(value);
22285         if(this.ddMatch && this.ddMatch.test(fvalue)){
22286             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22287             return false;
22288         }
22289         return true;
22290     },
22291
22292     // private
22293     // Provides logic to override the default TriggerField.validateBlur which just returns true
22294     validateBlur : function(){
22295         return !this.menu || !this.menu.isVisible();
22296     },
22297
22298     /**
22299      * Returns the current date value of the date field.
22300      * @return {Date} The date value
22301      */
22302     getValue : function(){
22303         
22304         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22305     },
22306
22307     /**
22308      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22309      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22310      * (the default format used is "m/d/y").
22311      * <br />Usage:
22312      * <pre><code>
22313 //All of these calls set the same date value (May 4, 2006)
22314
22315 //Pass a date object:
22316 var dt = new Date('5/4/06');
22317 dateField.setValue(dt);
22318
22319 //Pass a date string (default format):
22320 dateField.setValue('5/4/06');
22321
22322 //Pass a date string (custom format):
22323 dateField.format = 'Y-m-d';
22324 dateField.setValue('2006-5-4');
22325 </code></pre>
22326      * @param {String/Date} date The date or valid date string
22327      */
22328     setValue : function(date){
22329         if (this.hiddenField) {
22330             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22331         }
22332         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22333     },
22334
22335     // private
22336     parseDate : function(value){
22337         if(!value || value instanceof Date){
22338             return value;
22339         }
22340         var v = Date.parseDate(value, this.format);
22341         if(!v && this.altFormats){
22342             if(!this.altFormatsArray){
22343                 this.altFormatsArray = this.altFormats.split("|");
22344             }
22345             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22346                 v = Date.parseDate(value, this.altFormatsArray[i]);
22347             }
22348         }
22349         return v;
22350     },
22351
22352     // private
22353     formatDate : function(date, fmt){
22354         return (!date || !(date instanceof Date)) ?
22355                date : date.dateFormat(fmt || this.format);
22356     },
22357
22358     // private
22359     menuListeners : {
22360         select: function(m, d){
22361             this.setValue(d);
22362             this.fireEvent('select', this, d);
22363         },
22364         show : function(){ // retain focus styling
22365             this.onFocus();
22366         },
22367         hide : function(){
22368             this.focus.defer(10, this);
22369             var ml = this.menuListeners;
22370             this.menu.un("select", ml.select,  this);
22371             this.menu.un("show", ml.show,  this);
22372             this.menu.un("hide", ml.hide,  this);
22373         }
22374     },
22375
22376     // private
22377     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22378     onTriggerClick : function(){
22379         if(this.disabled){
22380             return;
22381         }
22382         if(this.menu == null){
22383             this.menu = new Roo.menu.DateMenu();
22384         }
22385         Roo.apply(this.menu.picker,  {
22386             showClear: this.allowBlank,
22387             minDate : this.minValue,
22388             maxDate : this.maxValue,
22389             disabledDatesRE : this.ddMatch,
22390             disabledDatesText : this.disabledDatesText,
22391             disabledDays : this.disabledDays,
22392             disabledDaysText : this.disabledDaysText,
22393             format : this.format,
22394             minText : String.format(this.minText, this.formatDate(this.minValue)),
22395             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22396         });
22397         this.menu.on(Roo.apply({}, this.menuListeners, {
22398             scope:this
22399         }));
22400         this.menu.picker.setValue(this.getValue() || new Date());
22401         this.menu.show(this.el, "tl-bl?");
22402     },
22403
22404     beforeBlur : function(){
22405         var v = this.parseDate(this.getRawValue());
22406         if(v){
22407             this.setValue(v);
22408         }
22409     }
22410
22411     /** @cfg {Boolean} grow @hide */
22412     /** @cfg {Number} growMin @hide */
22413     /** @cfg {Number} growMax @hide */
22414     /**
22415      * @hide
22416      * @method autoSize
22417      */
22418 });/*
22419  * Based on:
22420  * Ext JS Library 1.1.1
22421  * Copyright(c) 2006-2007, Ext JS, LLC.
22422  *
22423  * Originally Released Under LGPL - original licence link has changed is not relivant.
22424  *
22425  * Fork - LGPL
22426  * <script type="text/javascript">
22427  */
22428  
22429
22430 /**
22431  * @class Roo.form.ComboBox
22432  * @extends Roo.form.TriggerField
22433  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22434  * @constructor
22435  * Create a new ComboBox.
22436  * @param {Object} config Configuration options
22437  */
22438 Roo.form.ComboBox = function(config){
22439     Roo.form.ComboBox.superclass.constructor.call(this, config);
22440     this.addEvents({
22441         /**
22442          * @event expand
22443          * Fires when the dropdown list is expanded
22444              * @param {Roo.form.ComboBox} combo This combo box
22445              */
22446         'expand' : true,
22447         /**
22448          * @event collapse
22449          * Fires when the dropdown list is collapsed
22450              * @param {Roo.form.ComboBox} combo This combo box
22451              */
22452         'collapse' : true,
22453         /**
22454          * @event beforeselect
22455          * Fires before a list item is selected. Return false to cancel the selection.
22456              * @param {Roo.form.ComboBox} combo This combo box
22457              * @param {Roo.data.Record} record The data record returned from the underlying store
22458              * @param {Number} index The index of the selected item in the dropdown list
22459              */
22460         'beforeselect' : true,
22461         /**
22462          * @event select
22463          * Fires when a list item is selected
22464              * @param {Roo.form.ComboBox} combo This combo box
22465              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22466              * @param {Number} index The index of the selected item in the dropdown list
22467              */
22468         'select' : true,
22469         /**
22470          * @event beforequery
22471          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22472          * The event object passed has these properties:
22473              * @param {Roo.form.ComboBox} combo This combo box
22474              * @param {String} query The query
22475              * @param {Boolean} forceAll true to force "all" query
22476              * @param {Boolean} cancel true to cancel the query
22477              * @param {Object} e The query event object
22478              */
22479         'beforequery': true,
22480          /**
22481          * @event add
22482          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22483              * @param {Roo.form.ComboBox} combo This combo box
22484              */
22485         'add' : true,
22486         /**
22487          * @event edit
22488          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22489              * @param {Roo.form.ComboBox} combo This combo box
22490              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22491              */
22492         'edit' : true
22493         
22494         
22495     });
22496     if(this.transform){
22497         this.allowDomMove = false;
22498         var s = Roo.getDom(this.transform);
22499         if(!this.hiddenName){
22500             this.hiddenName = s.name;
22501         }
22502         if(!this.store){
22503             this.mode = 'local';
22504             var d = [], opts = s.options;
22505             for(var i = 0, len = opts.length;i < len; i++){
22506                 var o = opts[i];
22507                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22508                 if(o.selected) {
22509                     this.value = value;
22510                 }
22511                 d.push([value, o.text]);
22512             }
22513             this.store = new Roo.data.SimpleStore({
22514                 'id': 0,
22515                 fields: ['value', 'text'],
22516                 data : d
22517             });
22518             this.valueField = 'value';
22519             this.displayField = 'text';
22520         }
22521         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22522         if(!this.lazyRender){
22523             this.target = true;
22524             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22525             s.parentNode.removeChild(s); // remove it
22526             this.render(this.el.parentNode);
22527         }else{
22528             s.parentNode.removeChild(s); // remove it
22529         }
22530
22531     }
22532     if (this.store) {
22533         this.store = Roo.factory(this.store, Roo.data);
22534     }
22535     
22536     this.selectedIndex = -1;
22537     if(this.mode == 'local'){
22538         if(config.queryDelay === undefined){
22539             this.queryDelay = 10;
22540         }
22541         if(config.minChars === undefined){
22542             this.minChars = 0;
22543         }
22544     }
22545 };
22546
22547 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22548     /**
22549      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22550      */
22551     /**
22552      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22553      * rendering into an Roo.Editor, defaults to false)
22554      */
22555     /**
22556      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22557      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22558      */
22559     /**
22560      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22561      */
22562     /**
22563      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22564      * the dropdown list (defaults to undefined, with no header element)
22565      */
22566
22567      /**
22568      * @cfg {String/Roo.Template} tpl The template to use to render the output
22569      */
22570      
22571     // private
22572     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22573     /**
22574      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22575      */
22576     listWidth: undefined,
22577     /**
22578      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22579      * mode = 'remote' or 'text' if mode = 'local')
22580      */
22581     displayField: undefined,
22582     /**
22583      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22584      * mode = 'remote' or 'value' if mode = 'local'). 
22585      * Note: use of a valueField requires the user make a selection
22586      * in order for a value to be mapped.
22587      */
22588     valueField: undefined,
22589     /**
22590      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22591      * field's data value (defaults to the underlying DOM element's name)
22592      */
22593     hiddenName: undefined,
22594     /**
22595      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22596      */
22597     listClass: '',
22598     /**
22599      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22600      */
22601     selectedClass: 'x-combo-selected',
22602     /**
22603      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22604      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22605      * which displays a downward arrow icon).
22606      */
22607     triggerClass : 'x-form-arrow-trigger',
22608     /**
22609      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22610      */
22611     shadow:'sides',
22612     /**
22613      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22614      * anchor positions (defaults to 'tl-bl')
22615      */
22616     listAlign: 'tl-bl?',
22617     /**
22618      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22619      */
22620     maxHeight: 300,
22621     /**
22622      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22623      * query specified by the allQuery config option (defaults to 'query')
22624      */
22625     triggerAction: 'query',
22626     /**
22627      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22628      * (defaults to 4, does not apply if editable = false)
22629      */
22630     minChars : 4,
22631     /**
22632      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22633      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22634      */
22635     typeAhead: false,
22636     /**
22637      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22638      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22639      */
22640     queryDelay: 500,
22641     /**
22642      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22643      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22644      */
22645     pageSize: 0,
22646     /**
22647      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22648      * when editable = true (defaults to false)
22649      */
22650     selectOnFocus:false,
22651     /**
22652      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22653      */
22654     queryParam: 'query',
22655     /**
22656      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22657      * when mode = 'remote' (defaults to 'Loading...')
22658      */
22659     loadingText: 'Loading...',
22660     /**
22661      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22662      */
22663     resizable: false,
22664     /**
22665      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22666      */
22667     handleHeight : 8,
22668     /**
22669      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22670      * traditional select (defaults to true)
22671      */
22672     editable: true,
22673     /**
22674      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22675      */
22676     allQuery: '',
22677     /**
22678      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22679      */
22680     mode: 'remote',
22681     /**
22682      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22683      * listWidth has a higher value)
22684      */
22685     minListWidth : 70,
22686     /**
22687      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22688      * allow the user to set arbitrary text into the field (defaults to false)
22689      */
22690     forceSelection:false,
22691     /**
22692      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22693      * if typeAhead = true (defaults to 250)
22694      */
22695     typeAheadDelay : 250,
22696     /**
22697      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22698      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22699      */
22700     valueNotFoundText : undefined,
22701     /**
22702      * @cfg {bool} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22703      */
22704     blockFocus : false,
22705     
22706     /**
22707      * @cfg {bool} disableClear Disable showing of clear button.
22708      */
22709     disableClear : false,
22710     
22711     //private
22712     addicon : false,
22713     editicon: false,
22714     
22715     
22716     // private
22717     onRender : function(ct, position){
22718         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22719         if(this.hiddenName){
22720             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22721                     'before', true);
22722             this.hiddenField.value =
22723                 this.hiddenValue !== undefined ? this.hiddenValue :
22724                 this.value !== undefined ? this.value : '';
22725
22726             // prevent input submission
22727             this.el.dom.removeAttribute('name');
22728         }
22729         if(Roo.isGecko){
22730             this.el.dom.setAttribute('autocomplete', 'off');
22731         }
22732
22733         var cls = 'x-combo-list';
22734
22735         this.list = new Roo.Layer({
22736             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22737         });
22738
22739         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22740         this.list.setWidth(lw);
22741         this.list.swallowEvent('mousewheel');
22742         this.assetHeight = 0;
22743
22744         if(this.title){
22745             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22746             this.assetHeight += this.header.getHeight();
22747         }
22748
22749         this.innerList = this.list.createChild({cls:cls+'-inner'});
22750         this.innerList.on('mouseover', this.onViewOver, this);
22751         this.innerList.on('mousemove', this.onViewMove, this);
22752         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22753         
22754         if(this.allowBlank && !this.pageSize && !this.disableClear){
22755             this.footer = this.list.createChild({cls:cls+'-ft'});
22756             this.pageTb = new Roo.Toolbar(this.footer);
22757            
22758         }
22759         if(this.pageSize){
22760             this.footer = this.list.createChild({cls:cls+'-ft'});
22761             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22762                     {pageSize: this.pageSize});
22763             
22764         }
22765         
22766         if (this.pageTb && this.allowBlank && !this.disableClear) {
22767             var _this = this;
22768             this.pageTb.add(new Roo.Toolbar.Fill(), {
22769                 cls: 'x-btn-icon x-btn-clear',
22770                 text: '&#160;',
22771                 handler: function()
22772                 {
22773                     _this.collapse();
22774                     _this.clearValue();
22775                     _this.onSelect(false, -1);
22776                 }
22777             });
22778         }
22779         if (this.footer) {
22780             this.assetHeight += this.footer.getHeight();
22781         }
22782         
22783
22784         if(!this.tpl){
22785             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22786         }
22787
22788         this.view = new Roo.View(this.innerList, this.tpl, {
22789             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22790         });
22791
22792         this.view.on('click', this.onViewClick, this);
22793
22794         this.store.on('beforeload', this.onBeforeLoad, this);
22795         this.store.on('load', this.onLoad, this);
22796         this.store.on('loadexception', this.collapse, this);
22797
22798         if(this.resizable){
22799             this.resizer = new Roo.Resizable(this.list,  {
22800                pinned:true, handles:'se'
22801             });
22802             this.resizer.on('resize', function(r, w, h){
22803                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22804                 this.listWidth = w;
22805                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
22806                 this.restrictHeight();
22807             }, this);
22808             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
22809         }
22810         if(!this.editable){
22811             this.editable = true;
22812             this.setEditable(false);
22813         }  
22814         
22815         
22816         if (typeof(this.events.add.listeners) != 'undefined') {
22817             
22818             this.addicon = this.wrap.createChild(
22819                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
22820        
22821             this.addicon.on('click', function(e) {
22822                 this.fireEvent('add', this);
22823             }, this);
22824         }
22825         if (typeof(this.events.edit.listeners) != 'undefined') {
22826             
22827             this.editicon = this.wrap.createChild(
22828                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
22829             if (this.addicon) {
22830                 this.editicon.setStyle('margin-left', '40px');
22831             }
22832             this.editicon.on('click', function(e) {
22833                 
22834                 // we fire even  if inothing is selected..
22835                 this.fireEvent('edit', this, this.lastData );
22836                 
22837             }, this);
22838         }
22839         
22840         
22841         
22842     },
22843
22844     // private
22845     initEvents : function(){
22846         Roo.form.ComboBox.superclass.initEvents.call(this);
22847
22848         this.keyNav = new Roo.KeyNav(this.el, {
22849             "up" : function(e){
22850                 this.inKeyMode = true;
22851                 this.selectPrev();
22852             },
22853
22854             "down" : function(e){
22855                 if(!this.isExpanded()){
22856                     this.onTriggerClick();
22857                 }else{
22858                     this.inKeyMode = true;
22859                     this.selectNext();
22860                 }
22861             },
22862
22863             "enter" : function(e){
22864                 this.onViewClick();
22865                 //return true;
22866             },
22867
22868             "esc" : function(e){
22869                 this.collapse();
22870             },
22871
22872             "tab" : function(e){
22873                 this.onViewClick(false);
22874                 return true;
22875             },
22876
22877             scope : this,
22878
22879             doRelay : function(foo, bar, hname){
22880                 if(hname == 'down' || this.scope.isExpanded()){
22881                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22882                 }
22883                 return true;
22884             },
22885
22886             forceKeyDown: true
22887         });
22888         this.queryDelay = Math.max(this.queryDelay || 10,
22889                 this.mode == 'local' ? 10 : 250);
22890         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
22891         if(this.typeAhead){
22892             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
22893         }
22894         if(this.editable !== false){
22895             this.el.on("keyup", this.onKeyUp, this);
22896         }
22897         if(this.forceSelection){
22898             this.on('blur', this.doForce, this);
22899         }
22900     },
22901
22902     onDestroy : function(){
22903         if(this.view){
22904             this.view.setStore(null);
22905             this.view.el.removeAllListeners();
22906             this.view.el.remove();
22907             this.view.purgeListeners();
22908         }
22909         if(this.list){
22910             this.list.destroy();
22911         }
22912         if(this.store){
22913             this.store.un('beforeload', this.onBeforeLoad, this);
22914             this.store.un('load', this.onLoad, this);
22915             this.store.un('loadexception', this.collapse, this);
22916         }
22917         Roo.form.ComboBox.superclass.onDestroy.call(this);
22918     },
22919
22920     // private
22921     fireKey : function(e){
22922         if(e.isNavKeyPress() && !this.list.isVisible()){
22923             this.fireEvent("specialkey", this, e);
22924         }
22925     },
22926
22927     // private
22928     onResize: function(w, h){
22929         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
22930         
22931         if(typeof w != 'number'){
22932             // we do not handle it!?!?
22933             return;
22934         }
22935         var tw = this.trigger.getWidth();
22936         tw += this.addicon ? this.addicon.getWidth() : 0;
22937         tw += this.editicon ? this.editicon.getWidth() : 0;
22938         var x = w - tw;
22939         this.el.setWidth( this.adjustWidth('input', x));
22940             
22941         this.trigger.setStyle('left', x+'px');
22942         
22943         if(this.list && this.listWidth === undefined){
22944             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
22945             this.list.setWidth(lw);
22946             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22947         }
22948         
22949     
22950         
22951     },
22952
22953     /**
22954      * Allow or prevent the user from directly editing the field text.  If false is passed,
22955      * the user will only be able to select from the items defined in the dropdown list.  This method
22956      * is the runtime equivalent of setting the 'editable' config option at config time.
22957      * @param {Boolean} value True to allow the user to directly edit the field text
22958      */
22959     setEditable : function(value){
22960         if(value == this.editable){
22961             return;
22962         }
22963         this.editable = value;
22964         if(!value){
22965             this.el.dom.setAttribute('readOnly', true);
22966             this.el.on('mousedown', this.onTriggerClick,  this);
22967             this.el.addClass('x-combo-noedit');
22968         }else{
22969             this.el.dom.setAttribute('readOnly', false);
22970             this.el.un('mousedown', this.onTriggerClick,  this);
22971             this.el.removeClass('x-combo-noedit');
22972         }
22973     },
22974
22975     // private
22976     onBeforeLoad : function(){
22977         if(!this.hasFocus){
22978             return;
22979         }
22980         this.innerList.update(this.loadingText ?
22981                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
22982         this.restrictHeight();
22983         this.selectedIndex = -1;
22984     },
22985
22986     // private
22987     onLoad : function(){
22988         if(!this.hasFocus){
22989             return;
22990         }
22991         if(this.store.getCount() > 0){
22992             this.expand();
22993             this.restrictHeight();
22994             if(this.lastQuery == this.allQuery){
22995                 if(this.editable){
22996                     this.el.dom.select();
22997                 }
22998                 if(!this.selectByValue(this.value, true)){
22999                     this.select(0, true);
23000                 }
23001             }else{
23002                 this.selectNext();
23003                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23004                     this.taTask.delay(this.typeAheadDelay);
23005                 }
23006             }
23007         }else{
23008             this.onEmptyResults();
23009         }
23010         //this.el.focus();
23011     },
23012
23013     // private
23014     onTypeAhead : function(){
23015         if(this.store.getCount() > 0){
23016             var r = this.store.getAt(0);
23017             var newValue = r.data[this.displayField];
23018             var len = newValue.length;
23019             var selStart = this.getRawValue().length;
23020             if(selStart != len){
23021                 this.setRawValue(newValue);
23022                 this.selectText(selStart, newValue.length);
23023             }
23024         }
23025     },
23026
23027     // private
23028     onSelect : function(record, index){
23029         if(this.fireEvent('beforeselect', this, record, index) !== false){
23030             this.setFromData(index > -1 ? record.data : false);
23031             this.collapse();
23032             this.fireEvent('select', this, record, index);
23033         }
23034     },
23035
23036     /**
23037      * Returns the currently selected field value or empty string if no value is set.
23038      * @return {String} value The selected value
23039      */
23040     getValue : function(){
23041         if(this.valueField){
23042             return typeof this.value != 'undefined' ? this.value : '';
23043         }else{
23044             return Roo.form.ComboBox.superclass.getValue.call(this);
23045         }
23046     },
23047
23048     /**
23049      * Clears any text/value currently set in the field
23050      */
23051     clearValue : function(){
23052         if(this.hiddenField){
23053             this.hiddenField.value = '';
23054         }
23055         this.value = '';
23056         this.setRawValue('');
23057         this.lastSelectionText = '';
23058         this.applyEmptyText();
23059     },
23060
23061     /**
23062      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23063      * will be displayed in the field.  If the value does not match the data value of an existing item,
23064      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23065      * Otherwise the field will be blank (although the value will still be set).
23066      * @param {String} value The value to match
23067      */
23068     setValue : function(v){
23069         var text = v;
23070         if(this.valueField){
23071             var r = this.findRecord(this.valueField, v);
23072             if(r){
23073                 text = r.data[this.displayField];
23074             }else if(this.valueNotFoundText !== undefined){
23075                 text = this.valueNotFoundText;
23076             }
23077         }
23078         this.lastSelectionText = text;
23079         if(this.hiddenField){
23080             this.hiddenField.value = v;
23081         }
23082         Roo.form.ComboBox.superclass.setValue.call(this, text);
23083         this.value = v;
23084     },
23085     /**
23086      * @property {Object} the last set data for the element
23087      */
23088     
23089     lastData : false,
23090     /**
23091      * Sets the value of the field based on a object which is related to the record format for the store.
23092      * @param {Object} value the value to set as. or false on reset?
23093      */
23094     setFromData : function(o){
23095         var dv = ''; // display value
23096         var vv = ''; // value value..
23097         this.lastData = o;
23098         if (this.displayField) {
23099             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23100         } else {
23101             // this is an error condition!!!
23102             console.log('no value field set for '+ this.name);
23103         }
23104         
23105         if(this.valueField){
23106             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23107         }
23108         if(this.hiddenField){
23109             this.hiddenField.value = vv;
23110             
23111             this.lastSelectionText = dv;
23112             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23113             this.value = vv;
23114             return;
23115         }
23116         // no hidden field.. - we store the value in 'value', but still display
23117         // display field!!!!
23118         this.lastSelectionText = dv;
23119         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23120         this.value = vv;
23121         
23122         
23123     },
23124     // private
23125     reset : function(){
23126         // overridden so that last data is reset..
23127         this.setValue(this.originalValue);
23128         this.clearInvalid();
23129         this.lastData = false;
23130     },
23131     // private
23132     findRecord : function(prop, value){
23133         var record;
23134         if(this.store.getCount() > 0){
23135             this.store.each(function(r){
23136                 if(r.data[prop] == value){
23137                     record = r;
23138                     return false;
23139                 }
23140             });
23141         }
23142         return record;
23143     },
23144
23145     // private
23146     onViewMove : function(e, t){
23147         this.inKeyMode = false;
23148     },
23149
23150     // private
23151     onViewOver : function(e, t){
23152         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23153             return;
23154         }
23155         var item = this.view.findItemFromChild(t);
23156         if(item){
23157             var index = this.view.indexOf(item);
23158             this.select(index, false);
23159         }
23160     },
23161
23162     // private
23163     onViewClick : function(doFocus){
23164         var index = this.view.getSelectedIndexes()[0];
23165         var r = this.store.getAt(index);
23166         if(r){
23167             this.onSelect(r, index);
23168         }
23169         if(doFocus !== false && !this.blockFocus){
23170             this.el.focus();
23171         }
23172     },
23173
23174     // private
23175     restrictHeight : function(){
23176         this.innerList.dom.style.height = '';
23177         var inner = this.innerList.dom;
23178         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23179         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23180         this.list.beginUpdate();
23181         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23182         this.list.alignTo(this.el, this.listAlign);
23183         this.list.endUpdate();
23184     },
23185
23186     // private
23187     onEmptyResults : function(){
23188         this.collapse();
23189     },
23190
23191     /**
23192      * Returns true if the dropdown list is expanded, else false.
23193      */
23194     isExpanded : function(){
23195         return this.list.isVisible();
23196     },
23197
23198     /**
23199      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23200      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23201      * @param {String} value The data value of the item to select
23202      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23203      * selected item if it is not currently in view (defaults to true)
23204      * @return {Boolean} True if the value matched an item in the list, else false
23205      */
23206     selectByValue : function(v, scrollIntoView){
23207         if(v !== undefined && v !== null){
23208             var r = this.findRecord(this.valueField || this.displayField, v);
23209             if(r){
23210                 this.select(this.store.indexOf(r), scrollIntoView);
23211                 return true;
23212             }
23213         }
23214         return false;
23215     },
23216
23217     /**
23218      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23219      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23220      * @param {Number} index The zero-based index of the list item to select
23221      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23222      * selected item if it is not currently in view (defaults to true)
23223      */
23224     select : function(index, scrollIntoView){
23225         this.selectedIndex = index;
23226         this.view.select(index);
23227         if(scrollIntoView !== false){
23228             var el = this.view.getNode(index);
23229             if(el){
23230                 this.innerList.scrollChildIntoView(el, false);
23231             }
23232         }
23233     },
23234
23235     // private
23236     selectNext : function(){
23237         var ct = this.store.getCount();
23238         if(ct > 0){
23239             if(this.selectedIndex == -1){
23240                 this.select(0);
23241             }else if(this.selectedIndex < ct-1){
23242                 this.select(this.selectedIndex+1);
23243             }
23244         }
23245     },
23246
23247     // private
23248     selectPrev : function(){
23249         var ct = this.store.getCount();
23250         if(ct > 0){
23251             if(this.selectedIndex == -1){
23252                 this.select(0);
23253             }else if(this.selectedIndex != 0){
23254                 this.select(this.selectedIndex-1);
23255             }
23256         }
23257     },
23258
23259     // private
23260     onKeyUp : function(e){
23261         if(this.editable !== false && !e.isSpecialKey()){
23262             this.lastKey = e.getKey();
23263             this.dqTask.delay(this.queryDelay);
23264         }
23265     },
23266
23267     // private
23268     validateBlur : function(){
23269         return !this.list || !this.list.isVisible();   
23270     },
23271
23272     // private
23273     initQuery : function(){
23274         this.doQuery(this.getRawValue());
23275     },
23276
23277     // private
23278     doForce : function(){
23279         if(this.el.dom.value.length > 0){
23280             this.el.dom.value =
23281                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23282             this.applyEmptyText();
23283         }
23284     },
23285
23286     /**
23287      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23288      * query allowing the query action to be canceled if needed.
23289      * @param {String} query The SQL query to execute
23290      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23291      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23292      * saved in the current store (defaults to false)
23293      */
23294     doQuery : function(q, forceAll){
23295         if(q === undefined || q === null){
23296             q = '';
23297         }
23298         var qe = {
23299             query: q,
23300             forceAll: forceAll,
23301             combo: this,
23302             cancel:false
23303         };
23304         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23305             return false;
23306         }
23307         q = qe.query;
23308         forceAll = qe.forceAll;
23309         if(forceAll === true || (q.length >= this.minChars)){
23310             if(this.lastQuery != q){
23311                 this.lastQuery = q;
23312                 if(this.mode == 'local'){
23313                     this.selectedIndex = -1;
23314                     if(forceAll){
23315                         this.store.clearFilter();
23316                     }else{
23317                         this.store.filter(this.displayField, q);
23318                     }
23319                     this.onLoad();
23320                 }else{
23321                     this.store.baseParams[this.queryParam] = q;
23322                     this.store.load({
23323                         params: this.getParams(q)
23324                     });
23325                     this.expand();
23326                 }
23327             }else{
23328                 this.selectedIndex = -1;
23329                 this.onLoad();   
23330             }
23331         }
23332     },
23333
23334     // private
23335     getParams : function(q){
23336         var p = {};
23337         //p[this.queryParam] = q;
23338         if(this.pageSize){
23339             p.start = 0;
23340             p.limit = this.pageSize;
23341         }
23342         return p;
23343     },
23344
23345     /**
23346      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23347      */
23348     collapse : function(){
23349         if(!this.isExpanded()){
23350             return;
23351         }
23352         this.list.hide();
23353         Roo.get(document).un('mousedown', this.collapseIf, this);
23354         Roo.get(document).un('mousewheel', this.collapseIf, this);
23355         if (!this.editable) {
23356             Roo.get(document).un('keydown', this.listKeyPress, this);
23357         }
23358         this.fireEvent('collapse', this);
23359     },
23360
23361     // private
23362     collapseIf : function(e){
23363         if(!e.within(this.wrap) && !e.within(this.list)){
23364             this.collapse();
23365         }
23366     },
23367
23368     /**
23369      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23370      */
23371     expand : function(){
23372         if(this.isExpanded() || !this.hasFocus){
23373             return;
23374         }
23375         this.list.alignTo(this.el, this.listAlign);
23376         this.list.show();
23377         Roo.get(document).on('mousedown', this.collapseIf, this);
23378         Roo.get(document).on('mousewheel', this.collapseIf, this);
23379         if (!this.editable) {
23380             Roo.get(document).on('keydown', this.listKeyPress, this);
23381         }
23382         
23383         this.fireEvent('expand', this);
23384     },
23385
23386     // private
23387     // Implements the default empty TriggerField.onTriggerClick function
23388     onTriggerClick : function(){
23389         if(this.disabled){
23390             return;
23391         }
23392         if(this.isExpanded()){
23393             this.collapse();
23394             if (!this.blockFocus) {
23395                 this.el.focus();
23396             }
23397             
23398         }else {
23399             this.hasFocus = true;
23400             if(this.triggerAction == 'all') {
23401                 this.doQuery(this.allQuery, true);
23402             } else {
23403                 this.doQuery(this.getRawValue());
23404             }
23405             if (!this.blockFocus) {
23406                 this.el.focus();
23407             }
23408         }
23409     },
23410     listKeyPress : function(e)
23411     {
23412         //Roo.log('listkeypress');
23413         // scroll to first matching element based on key pres..
23414         if (e.isSpecialKey()) {
23415             return false;
23416         }
23417         var k = String.fromCharCode(e.getKey()).toUpperCase();
23418         //Roo.log(k);
23419         var match  = false
23420         this.store.each(function(v) { 
23421             if (v.get(combo.displayField).substring(0,1).toUpperCase() == k) {
23422                 match = this.store.indexOf(v);
23423                 return false;
23424             }
23425         }, this);
23426         
23427         if (match === false) {
23428             return true; // no more action?
23429         }
23430         // scroll to?
23431         this.view.select(match);
23432         var sn = Roo.get(combo.view.getSelectedNodes()[0])
23433         sn.scrollIntoView(sn.dom.parentNode, false);
23434     }
23435
23436     /** 
23437     * @cfg {Boolean} grow 
23438     * @hide 
23439     */
23440     /** 
23441     * @cfg {Number} growMin 
23442     * @hide 
23443     */
23444     /** 
23445     * @cfg {Number} growMax 
23446     * @hide 
23447     */
23448     /**
23449      * @hide
23450      * @method autoSize
23451      */
23452 });/*
23453  * Based on:
23454  * Ext JS Library 1.1.1
23455  * Copyright(c) 2006-2007, Ext JS, LLC.
23456  *
23457  * Originally Released Under LGPL - original licence link has changed is not relivant.
23458  *
23459  * Fork - LGPL
23460  * <script type="text/javascript">
23461  */
23462 /**
23463  * @class Roo.form.Checkbox
23464  * @extends Roo.form.Field
23465  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23466  * @constructor
23467  * Creates a new Checkbox
23468  * @param {Object} config Configuration options
23469  */
23470 Roo.form.Checkbox = function(config){
23471     Roo.form.Checkbox.superclass.constructor.call(this, config);
23472     this.addEvents({
23473         /**
23474          * @event check
23475          * Fires when the checkbox is checked or unchecked.
23476              * @param {Roo.form.Checkbox} this This checkbox
23477              * @param {Boolean} checked The new checked value
23478              */
23479         check : true
23480     });
23481 };
23482
23483 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23484     /**
23485      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23486      */
23487     focusClass : undefined,
23488     /**
23489      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23490      */
23491     fieldClass: "x-form-field",
23492     /**
23493      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23494      */
23495     checked: false,
23496     /**
23497      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23498      * {tag: "input", type: "checkbox", autocomplete: "off"})
23499      */
23500     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23501     /**
23502      * @cfg {String} boxLabel The text that appears beside the checkbox
23503      */
23504     boxLabel : "",
23505     /**
23506      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23507      */  
23508     inputValue : '1',
23509     /**
23510      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23511      */
23512      valueOff: '0', // value when not checked..
23513
23514     actionMode : 'viewEl', 
23515     //
23516     // private
23517     itemCls : 'x-menu-check-item x-form-item',
23518     groupClass : 'x-menu-group-item',
23519     inputType : 'hidden',
23520     
23521     
23522     inSetChecked: false, // check that we are not calling self...
23523     
23524     inputElement: false, // real input element?
23525     basedOn: false, // ????
23526     
23527     isFormField: true, // not sure where this is needed!!!!
23528
23529     onResize : function(){
23530         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23531         if(!this.boxLabel){
23532             this.el.alignTo(this.wrap, 'c-c');
23533         }
23534     },
23535
23536     initEvents : function(){
23537         Roo.form.Checkbox.superclass.initEvents.call(this);
23538         this.el.on("click", this.onClick,  this);
23539         this.el.on("change", this.onClick,  this);
23540     },
23541
23542
23543     getResizeEl : function(){
23544         return this.wrap;
23545     },
23546
23547     getPositionEl : function(){
23548         return this.wrap;
23549     },
23550
23551     // private
23552     onRender : function(ct, position){
23553         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23554         /*
23555         if(this.inputValue !== undefined){
23556             this.el.dom.value = this.inputValue;
23557         }
23558         */
23559         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23560         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23561         var viewEl = this.wrap.createChild({ 
23562             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23563         this.viewEl = viewEl;   
23564         this.wrap.on('click', this.onClick,  this); 
23565         
23566         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23567         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23568         
23569         
23570         
23571         if(this.boxLabel){
23572             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23573         //    viewEl.on('click', this.onClick,  this); 
23574         }
23575         //if(this.checked){
23576             this.setChecked(this.checked);
23577         //}else{
23578             //this.checked = this.el.dom;
23579         //}
23580
23581     },
23582
23583     // private
23584     initValue : Roo.emptyFn,
23585
23586     /**
23587      * Returns the checked state of the checkbox.
23588      * @return {Boolean} True if checked, else false
23589      */
23590     getValue : function(){
23591         if(this.el){
23592             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23593         }
23594         return this.valueOff;
23595         
23596     },
23597
23598         // private
23599     onClick : function(){ 
23600         this.setChecked(!this.checked);
23601
23602         //if(this.el.dom.checked != this.checked){
23603         //    this.setValue(this.el.dom.checked);
23604        // }
23605     },
23606
23607     /**
23608      * Sets the checked state of the checkbox.
23609      * On is always based on a string comparison between inputValue and the param.
23610      * @param {Boolean/String} value - the value to set 
23611      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
23612      */
23613     setValue : function(v,suppressEvent){
23614         
23615         
23616         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23617         //if(this.el && this.el.dom){
23618         //    this.el.dom.checked = this.checked;
23619         //    this.el.dom.defaultChecked = this.checked;
23620         //}
23621         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
23622         //this.fireEvent("check", this, this.checked);
23623     },
23624     // private..
23625     setChecked : function(state,suppressEvent)
23626     {
23627         if (this.inSetChecked) {
23628             this.checked = state;
23629             return;
23630         }
23631         
23632     
23633         if(this.wrap){
23634             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23635         }
23636         this.checked = state;
23637         if(suppressEvent !== true){
23638             this.fireEvent('checkchange', this, state);
23639         }
23640         this.inSetChecked = true;
23641         this.el.dom.value = state ? this.inputValue : this.valueOff;
23642         this.inSetChecked = false;
23643         
23644     },
23645     // handle setting of hidden value by some other method!!?!?
23646     setFromHidden: function()
23647     {
23648         if(!this.el){
23649             return;
23650         }
23651         //console.log("SET FROM HIDDEN");
23652         //alert('setFrom hidden');
23653         this.setValue(this.el.dom.value);
23654     },
23655     
23656     onDestroy : function()
23657     {
23658         if(this.viewEl){
23659             Roo.get(this.viewEl).remove();
23660         }
23661          
23662         Roo.form.Checkbox.superclass.onDestroy.call(this);
23663     }
23664
23665 });/*
23666  * Based on:
23667  * Ext JS Library 1.1.1
23668  * Copyright(c) 2006-2007, Ext JS, LLC.
23669  *
23670  * Originally Released Under LGPL - original licence link has changed is not relivant.
23671  *
23672  * Fork - LGPL
23673  * <script type="text/javascript">
23674  */
23675  
23676 /**
23677  * @class Roo.form.Radio
23678  * @extends Roo.form.Checkbox
23679  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23680  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23681  * @constructor
23682  * Creates a new Radio
23683  * @param {Object} config Configuration options
23684  */
23685 Roo.form.Radio = function(){
23686     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23687 };
23688 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23689     inputType: 'radio',
23690
23691     /**
23692      * If this radio is part of a group, it will return the selected value
23693      * @return {String}
23694      */
23695     getGroupValue : function(){
23696         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23697     }
23698 });//<script type="text/javascript">
23699
23700 /*
23701  * Ext JS Library 1.1.1
23702  * Copyright(c) 2006-2007, Ext JS, LLC.
23703  * licensing@extjs.com
23704  * 
23705  * http://www.extjs.com/license
23706  */
23707  
23708  /*
23709   * 
23710   * Known bugs:
23711   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23712   * - IE ? - no idea how much works there.
23713   * 
23714   * 
23715   * 
23716   */
23717  
23718
23719 /**
23720  * @class Ext.form.HtmlEditor
23721  * @extends Ext.form.Field
23722  * Provides a lightweight HTML Editor component.
23723  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23724  * 
23725  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23726  * supported by this editor.</b><br/><br/>
23727  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23728  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23729  */
23730 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23731       /**
23732      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23733      */
23734     toolbars : false,
23735     /**
23736      * @cfg {String} createLinkText The default text for the create link prompt
23737      */
23738     createLinkText : 'Please enter the URL for the link:',
23739     /**
23740      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23741      */
23742     defaultLinkValue : 'http:/'+'/',
23743    
23744     
23745     // id of frame..
23746     frameId: false,
23747     
23748     // private properties
23749     validationEvent : false,
23750     deferHeight: true,
23751     initialized : false,
23752     activated : false,
23753     sourceEditMode : false,
23754     onFocus : Roo.emptyFn,
23755     iframePad:3,
23756     hideMode:'offsets',
23757     defaultAutoCreate : {
23758         tag: "textarea",
23759         style:"width:500px;height:300px;",
23760         autocomplete: "off"
23761     },
23762
23763     // private
23764     initComponent : function(){
23765         this.addEvents({
23766             /**
23767              * @event initialize
23768              * Fires when the editor is fully initialized (including the iframe)
23769              * @param {HtmlEditor} this
23770              */
23771             initialize: true,
23772             /**
23773              * @event activate
23774              * Fires when the editor is first receives the focus. Any insertion must wait
23775              * until after this event.
23776              * @param {HtmlEditor} this
23777              */
23778             activate: true,
23779              /**
23780              * @event beforesync
23781              * Fires before the textarea is updated with content from the editor iframe. Return false
23782              * to cancel the sync.
23783              * @param {HtmlEditor} this
23784              * @param {String} html
23785              */
23786             beforesync: true,
23787              /**
23788              * @event beforepush
23789              * Fires before the iframe editor is updated with content from the textarea. Return false
23790              * to cancel the push.
23791              * @param {HtmlEditor} this
23792              * @param {String} html
23793              */
23794             beforepush: true,
23795              /**
23796              * @event sync
23797              * Fires when the textarea is updated with content from the editor iframe.
23798              * @param {HtmlEditor} this
23799              * @param {String} html
23800              */
23801             sync: true,
23802              /**
23803              * @event push
23804              * Fires when the iframe editor is updated with content from the textarea.
23805              * @param {HtmlEditor} this
23806              * @param {String} html
23807              */
23808             push: true,
23809              /**
23810              * @event editmodechange
23811              * Fires when the editor switches edit modes
23812              * @param {HtmlEditor} this
23813              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23814              */
23815             editmodechange: true,
23816             /**
23817              * @event editorevent
23818              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23819              * @param {HtmlEditor} this
23820              */
23821             editorevent: true
23822         })
23823     },
23824
23825     /**
23826      * Protected method that will not generally be called directly. It
23827      * is called when the editor creates its toolbar. Override this method if you need to
23828      * add custom toolbar buttons.
23829      * @param {HtmlEditor} editor
23830      */
23831     createToolbar : function(editor){
23832         if (!editor.toolbars || !editor.toolbars.length) {
23833             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
23834         }
23835         
23836         for (var i =0 ; i < editor.toolbars.length;i++) {
23837             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
23838             editor.toolbars[i].init(editor);
23839         }
23840          
23841         
23842     },
23843
23844     /**
23845      * Protected method that will not generally be called directly. It
23846      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23847      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23848      */
23849     getDocMarkup : function(){
23850         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
23851     },
23852
23853     // private
23854     onRender : function(ct, position){
23855         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
23856         this.el.dom.style.border = '0 none';
23857         this.el.dom.setAttribute('tabIndex', -1);
23858         this.el.addClass('x-hidden');
23859         if(Roo.isIE){ // fix IE 1px bogus margin
23860             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23861         }
23862         this.wrap = this.el.wrap({
23863             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23864         });
23865
23866         this.frameId = Roo.id();
23867         this.createToolbar(this);
23868         
23869         
23870         
23871         
23872       
23873         
23874         var iframe = this.wrap.createChild({
23875             tag: 'iframe',
23876             id: this.frameId,
23877             name: this.frameId,
23878             frameBorder : 'no',
23879             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23880         });
23881         
23882        // console.log(iframe);
23883         //this.wrap.dom.appendChild(iframe);
23884
23885         this.iframe = iframe.dom;
23886
23887          this.assignDocWin();
23888         
23889         this.doc.designMode = 'on';
23890        
23891         this.doc.open();
23892         this.doc.write(this.getDocMarkup());
23893         this.doc.close();
23894
23895         
23896         var task = { // must defer to wait for browser to be ready
23897             run : function(){
23898                 //console.log("run task?" + this.doc.readyState);
23899                 this.assignDocWin();
23900                 if(this.doc.body || this.doc.readyState == 'complete'){
23901                     try {
23902                         this.doc.designMode="on";
23903                     } catch (e) {
23904                         return;
23905                     }
23906                     Roo.TaskMgr.stop(task);
23907                     this.initEditor.defer(10, this);
23908                 }
23909             },
23910             interval : 10,
23911             duration:10000,
23912             scope: this
23913         };
23914         Roo.TaskMgr.start(task);
23915
23916         if(!this.width){
23917             this.setSize(this.el.getSize());
23918         }
23919     },
23920
23921     // private
23922     onResize : function(w, h){
23923         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
23924         if(this.el && this.iframe){
23925             if(typeof w == 'number'){
23926                 var aw = w - this.wrap.getFrameWidth('lr');
23927                 this.el.setWidth(this.adjustWidth('textarea', aw));
23928                 this.iframe.style.width = aw + 'px';
23929             }
23930             if(typeof h == 'number'){
23931                 var tbh = 0;
23932                 for (var i =0; i < this.toolbars.length;i++) {
23933                     // fixme - ask toolbars for heights?
23934                     tbh += this.toolbars[i].tb.el.getHeight();
23935                 }
23936                 
23937                 
23938                 
23939                 
23940                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23941                 this.el.setHeight(this.adjustWidth('textarea', ah));
23942                 this.iframe.style.height = ah + 'px';
23943                 if(this.doc){
23944                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
23945                 }
23946             }
23947         }
23948     },
23949
23950     /**
23951      * Toggles the editor between standard and source edit mode.
23952      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23953      */
23954     toggleSourceEdit : function(sourceEditMode){
23955         
23956         this.sourceEditMode = sourceEditMode === true;
23957         
23958         if(this.sourceEditMode){
23959           
23960             this.syncValue();
23961             this.iframe.className = 'x-hidden';
23962             this.el.removeClass('x-hidden');
23963             this.el.dom.removeAttribute('tabIndex');
23964             this.el.focus();
23965         }else{
23966              
23967             this.pushValue();
23968             this.iframe.className = '';
23969             this.el.addClass('x-hidden');
23970             this.el.dom.setAttribute('tabIndex', -1);
23971             this.deferFocus();
23972         }
23973         this.setSize(this.wrap.getSize());
23974         this.fireEvent('editmodechange', this, this.sourceEditMode);
23975     },
23976
23977     // private used internally
23978     createLink : function(){
23979         var url = prompt(this.createLinkText, this.defaultLinkValue);
23980         if(url && url != 'http:/'+'/'){
23981             this.relayCmd('createlink', url);
23982         }
23983     },
23984
23985     // private (for BoxComponent)
23986     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23987
23988     // private (for BoxComponent)
23989     getResizeEl : function(){
23990         return this.wrap;
23991     },
23992
23993     // private (for BoxComponent)
23994     getPositionEl : function(){
23995         return this.wrap;
23996     },
23997
23998     // private
23999     initEvents : function(){
24000         this.originalValue = this.getValue();
24001     },
24002
24003     /**
24004      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24005      * @method
24006      */
24007     markInvalid : Roo.emptyFn,
24008     /**
24009      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24010      * @method
24011      */
24012     clearInvalid : Roo.emptyFn,
24013
24014     setValue : function(v){
24015         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24016         this.pushValue();
24017     },
24018
24019     /**
24020      * Protected method that will not generally be called directly. If you need/want
24021      * custom HTML cleanup, this is the method you should override.
24022      * @param {String} html The HTML to be cleaned
24023      * return {String} The cleaned HTML
24024      */
24025     cleanHtml : function(html){
24026         html = String(html);
24027         if(html.length > 5){
24028             if(Roo.isSafari){ // strip safari nonsense
24029                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24030             }
24031         }
24032         if(html == '&nbsp;'){
24033             html = '';
24034         }
24035         return html;
24036     },
24037
24038     /**
24039      * Protected method that will not generally be called directly. Syncs the contents
24040      * of the editor iframe with the textarea.
24041      */
24042     syncValue : function(){
24043         if(this.initialized){
24044             var bd = (this.doc.body || this.doc.documentElement);
24045             var html = bd.innerHTML;
24046             if(Roo.isSafari){
24047                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24048                 var m = bs.match(/text-align:(.*?);/i);
24049                 if(m && m[1]){
24050                     html = '<div style="'+m[0]+'">' + html + '</div>';
24051                 }
24052             }
24053             html = this.cleanHtml(html);
24054             if(this.fireEvent('beforesync', this, html) !== false){
24055                 this.el.dom.value = html;
24056                 this.fireEvent('sync', this, html);
24057             }
24058         }
24059     },
24060
24061     /**
24062      * Protected method that will not generally be called directly. Pushes the value of the textarea
24063      * into the iframe editor.
24064      */
24065     pushValue : function(){
24066         if(this.initialized){
24067             var v = this.el.dom.value;
24068             if(v.length < 1){
24069                 v = '&#160;';
24070             }
24071             if(this.fireEvent('beforepush', this, v) !== false){
24072                 (this.doc.body || this.doc.documentElement).innerHTML = v;
24073                 this.fireEvent('push', this, v);
24074             }
24075         }
24076     },
24077
24078     // private
24079     deferFocus : function(){
24080         this.focus.defer(10, this);
24081     },
24082
24083     // doc'ed in Field
24084     focus : function(){
24085         if(this.win && !this.sourceEditMode){
24086             this.win.focus();
24087         }else{
24088             this.el.focus();
24089         }
24090     },
24091     
24092     assignDocWin: function()
24093     {
24094         var iframe = this.iframe;
24095         
24096          if(Roo.isIE){
24097             this.doc = iframe.contentWindow.document;
24098             this.win = iframe.contentWindow;
24099         } else {
24100             if (!Roo.get(this.frameId)) {
24101                 return;
24102             }
24103             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24104             this.win = Roo.get(this.frameId).dom.contentWindow;
24105         }
24106     },
24107     
24108     // private
24109     initEditor : function(){
24110         //console.log("INIT EDITOR");
24111         this.assignDocWin();
24112         
24113         
24114         
24115         this.doc.designMode="on";
24116         this.doc.open();
24117         this.doc.write(this.getDocMarkup());
24118         this.doc.close();
24119         
24120         var dbody = (this.doc.body || this.doc.documentElement);
24121         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24122         // this copies styles from the containing element into thsi one..
24123         // not sure why we need all of this..
24124         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24125         ss['background-attachment'] = 'fixed'; // w3c
24126         dbody.bgProperties = 'fixed'; // ie
24127         Roo.DomHelper.applyStyles(dbody, ss);
24128         Roo.EventManager.on(this.doc, {
24129             'mousedown': this.onEditorEvent,
24130             'dblclick': this.onEditorEvent,
24131             'click': this.onEditorEvent,
24132             'keyup': this.onEditorEvent,
24133             buffer:100,
24134             scope: this
24135         });
24136         if(Roo.isGecko){
24137             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24138         }
24139         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24140             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24141         }
24142         this.initialized = true;
24143
24144         this.fireEvent('initialize', this);
24145         this.pushValue();
24146     },
24147
24148     // private
24149     onDestroy : function(){
24150         
24151         
24152         
24153         if(this.rendered){
24154             
24155             for (var i =0; i < this.toolbars.length;i++) {
24156                 // fixme - ask toolbars for heights?
24157                 this.toolbars[i].onDestroy();
24158             }
24159             
24160             this.wrap.dom.innerHTML = '';
24161             this.wrap.remove();
24162         }
24163     },
24164
24165     // private
24166     onFirstFocus : function(){
24167         
24168         this.assignDocWin();
24169         
24170         
24171         this.activated = true;
24172         for (var i =0; i < this.toolbars.length;i++) {
24173             this.toolbars[i].onFirstFocus();
24174         }
24175        
24176         if(Roo.isGecko){ // prevent silly gecko errors
24177             this.win.focus();
24178             var s = this.win.getSelection();
24179             if(!s.focusNode || s.focusNode.nodeType != 3){
24180                 var r = s.getRangeAt(0);
24181                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24182                 r.collapse(true);
24183                 this.deferFocus();
24184             }
24185             try{
24186                 this.execCmd('useCSS', true);
24187                 this.execCmd('styleWithCSS', false);
24188             }catch(e){}
24189         }
24190         this.fireEvent('activate', this);
24191     },
24192
24193     // private
24194     adjustFont: function(btn){
24195         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24196         //if(Roo.isSafari){ // safari
24197         //    adjust *= 2;
24198        // }
24199         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24200         if(Roo.isSafari){ // safari
24201             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24202             v =  (v < 10) ? 10 : v;
24203             v =  (v > 48) ? 48 : v;
24204             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24205             
24206         }
24207         
24208         
24209         v = Math.max(1, v+adjust);
24210         
24211         this.execCmd('FontSize', v  );
24212     },
24213
24214     onEditorEvent : function(e){
24215         this.fireEvent('editorevent', this, e);
24216       //  this.updateToolbar();
24217         this.syncValue();
24218     },
24219
24220     insertTag : function(tg)
24221     {
24222         // could be a bit smarter... -> wrap the current selected tRoo..
24223         
24224         this.execCmd("formatblock",   tg);
24225         
24226     },
24227     
24228     insertText : function(txt)
24229     {
24230         
24231         
24232         range = this.createRange();
24233         range.deleteContents();
24234                //alert(Sender.getAttribute('label'));
24235                
24236         range.insertNode(this.doc.createTextNode(txt));
24237     } ,
24238     
24239     // private
24240     relayBtnCmd : function(btn){
24241         this.relayCmd(btn.cmd);
24242     },
24243
24244     /**
24245      * Executes a Midas editor command on the editor document and performs necessary focus and
24246      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24247      * @param {String} cmd The Midas command
24248      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24249      */
24250     relayCmd : function(cmd, value){
24251         this.win.focus();
24252         this.execCmd(cmd, value);
24253         this.fireEvent('editorevent', this);
24254         //this.updateToolbar();
24255         this.deferFocus();
24256     },
24257
24258     /**
24259      * Executes a Midas editor command directly on the editor document.
24260      * For visual commands, you should use {@link #relayCmd} instead.
24261      * <b>This should only be called after the editor is initialized.</b>
24262      * @param {String} cmd The Midas command
24263      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24264      */
24265     execCmd : function(cmd, value){
24266         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24267         this.syncValue();
24268     },
24269
24270    
24271     /**
24272      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24273      * to insert tRoo.
24274      * @param {String} text
24275      */
24276     insertAtCursor : function(text){
24277         if(!this.activated){
24278             return;
24279         }
24280         if(Roo.isIE){
24281             this.win.focus();
24282             var r = this.doc.selection.createRange();
24283             if(r){
24284                 r.collapse(true);
24285                 r.pasteHTML(text);
24286                 this.syncValue();
24287                 this.deferFocus();
24288             }
24289         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24290             this.win.focus();
24291             this.execCmd('InsertHTML', text);
24292             this.deferFocus();
24293         }
24294     },
24295  // private
24296     mozKeyPress : function(e){
24297         if(e.ctrlKey){
24298             var c = e.getCharCode(), cmd;
24299           
24300             if(c > 0){
24301                 c = String.fromCharCode(c).toLowerCase();
24302                 switch(c){
24303                     case 'b':
24304                         cmd = 'bold';
24305                     break;
24306                     case 'i':
24307                         cmd = 'italic';
24308                     break;
24309                     case 'u':
24310                         cmd = 'underline';
24311                     case 'v':
24312                         this.cleanUpPaste.defer(100, this);
24313                         return;
24314                     break;
24315                 }
24316                 if(cmd){
24317                     this.win.focus();
24318                     this.execCmd(cmd);
24319                     this.deferFocus();
24320                     e.preventDefault();
24321                 }
24322                 
24323             }
24324         }
24325     },
24326
24327     // private
24328     fixKeys : function(){ // load time branching for fastest keydown performance
24329         if(Roo.isIE){
24330             return function(e){
24331                 var k = e.getKey(), r;
24332                 if(k == e.TAB){
24333                     e.stopEvent();
24334                     r = this.doc.selection.createRange();
24335                     if(r){
24336                         r.collapse(true);
24337                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24338                         this.deferFocus();
24339                     }
24340                     return;
24341                 }
24342                 
24343                 if(k == e.ENTER){
24344                     r = this.doc.selection.createRange();
24345                     if(r){
24346                         var target = r.parentElement();
24347                         if(!target || target.tagName.toLowerCase() != 'li'){
24348                             e.stopEvent();
24349                             r.pasteHTML('<br />');
24350                             r.collapse(false);
24351                             r.select();
24352                         }
24353                     }
24354                 }
24355                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24356                     this.cleanUpPaste.defer(100, this);
24357                     return;
24358                 }
24359                 
24360                 
24361             };
24362         }else if(Roo.isOpera){
24363             return function(e){
24364                 var k = e.getKey();
24365                 if(k == e.TAB){
24366                     e.stopEvent();
24367                     this.win.focus();
24368                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24369                     this.deferFocus();
24370                 }
24371                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24372                     this.cleanUpPaste.defer(100, this);
24373                     return;
24374                 }
24375                 
24376             };
24377         }else if(Roo.isSafari){
24378             return function(e){
24379                 var k = e.getKey();
24380                 
24381                 if(k == e.TAB){
24382                     e.stopEvent();
24383                     this.execCmd('InsertText','\t');
24384                     this.deferFocus();
24385                     return;
24386                 }
24387                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24388                     this.cleanUpPaste.defer(100, this);
24389                     return;
24390                 }
24391                 
24392              };
24393         }
24394     }(),
24395     
24396     getAllAncestors: function()
24397     {
24398         var p = this.getSelectedNode();
24399         var a = [];
24400         if (!p) {
24401             a.push(p); // push blank onto stack..
24402             p = this.getParentElement();
24403         }
24404         
24405         
24406         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24407             a.push(p);
24408             p = p.parentNode;
24409         }
24410         a.push(this.doc.body);
24411         return a;
24412     },
24413     lastSel : false,
24414     lastSelNode : false,
24415     
24416     
24417     getSelection : function() 
24418     {
24419         this.assignDocWin();
24420         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24421     },
24422     
24423     getSelectedNode: function() 
24424     {
24425         // this may only work on Gecko!!!
24426         
24427         // should we cache this!!!!
24428         
24429         
24430         
24431          
24432         var range = this.createRange(this.getSelection());
24433         
24434         if (Roo.isIE) {
24435             var parent = range.parentElement();
24436             while (true) {
24437                 var testRange = range.duplicate();
24438                 testRange.moveToElementText(parent);
24439                 if (testRange.inRange(range)) {
24440                     break;
24441                 }
24442                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24443                     break;
24444                 }
24445                 parent = parent.parentElement;
24446             }
24447             return parent;
24448         }
24449         
24450         
24451         var ar = range.endContainer.childNodes;
24452         if (!ar.length) {
24453             ar = range.commonAncestorContainer.childNodes;
24454             //alert(ar.length);
24455         }
24456         var nodes = [];
24457         var other_nodes = [];
24458         var has_other_nodes = false;
24459         for (var i=0;i<ar.length;i++) {
24460             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24461                 continue;
24462             }
24463             // fullly contained node.
24464             
24465             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24466                 nodes.push(ar[i]);
24467                 continue;
24468             }
24469             
24470             // probably selected..
24471             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24472                 other_nodes.push(ar[i]);
24473                 continue;
24474             }
24475             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24476                 continue;
24477             }
24478             
24479             
24480             has_other_nodes = true;
24481         }
24482         if (!nodes.length && other_nodes.length) {
24483             nodes= other_nodes;
24484         }
24485         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24486             return false;
24487         }
24488         
24489         return nodes[0];
24490     },
24491     createRange: function(sel)
24492     {
24493         // this has strange effects when using with 
24494         // top toolbar - not sure if it's a great idea.
24495         //this.editor.contentWindow.focus();
24496         if (typeof sel != "undefined") {
24497             try {
24498                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24499             } catch(e) {
24500                 return this.doc.createRange();
24501             }
24502         } else {
24503             return this.doc.createRange();
24504         }
24505     },
24506     getParentElement: function()
24507     {
24508         
24509         this.assignDocWin();
24510         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24511         
24512         var range = this.createRange(sel);
24513          
24514         try {
24515             var p = range.commonAncestorContainer;
24516             while (p.nodeType == 3) { // text node
24517                 p = p.parentNode;
24518             }
24519             return p;
24520         } catch (e) {
24521             return null;
24522         }
24523     
24524     },
24525     
24526     
24527     
24528     // BC Hacks - cause I cant work out what i was trying to do..
24529     rangeIntersectsNode : function(range, node)
24530     {
24531         var nodeRange = node.ownerDocument.createRange();
24532         try {
24533             nodeRange.selectNode(node);
24534         }
24535         catch (e) {
24536             nodeRange.selectNodeContents(node);
24537         }
24538
24539         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24540                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24541     },
24542     rangeCompareNode : function(range, node) {
24543         var nodeRange = node.ownerDocument.createRange();
24544         try {
24545             nodeRange.selectNode(node);
24546         } catch (e) {
24547             nodeRange.selectNodeContents(node);
24548         }
24549         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24550         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24551
24552         if (nodeIsBefore && !nodeIsAfter)
24553             return 0;
24554         if (!nodeIsBefore && nodeIsAfter)
24555             return 1;
24556         if (nodeIsBefore && nodeIsAfter)
24557             return 2;
24558
24559         return 3;
24560     },
24561
24562     // private? - in a new class?
24563     cleanUpPaste :  function()
24564     {
24565         // cleans up the whole document..
24566       //  console.log('cleanuppaste');
24567         this.cleanUpChildren(this.doc.body)
24568         
24569         
24570     },
24571     cleanUpChildren : function (n)
24572     {
24573         if (!n.childNodes.length) {
24574             return;
24575         }
24576         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24577            this.cleanUpChild(n.childNodes[i]);
24578         }
24579     },
24580     
24581     
24582         
24583     
24584     cleanUpChild : function (node)
24585     {
24586         //console.log(node);
24587         if (node.nodeName == "#text") {
24588             // clean up silly Windows -- stuff?
24589             return; 
24590         }
24591         if (node.nodeName == "#comment") {
24592             node.parentNode.removeChild(node);
24593             // clean up silly Windows -- stuff?
24594             return; 
24595         }
24596         
24597         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
24598             // remove node.
24599             node.parentNode.removeChild(node);
24600             return;
24601             
24602         }
24603         if (!node.attributes || !node.attributes.length) {
24604             this.cleanUpChildren(node);
24605             return;
24606         }
24607         
24608         function cleanAttr(n,v)
24609         {
24610             
24611             if (v.match(/^\./) || v.match(/^\//)) {
24612                 return;
24613             }
24614             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
24615                 return;
24616             }
24617             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
24618             node.removeAttribute(n);
24619             
24620         }
24621         
24622         function cleanStyle(n,v)
24623         {
24624             if (v.match(/expression/)) { //XSS?? should we even bother..
24625                 node.removeAttribute(n);
24626                 return;
24627             }
24628             
24629             
24630             var parts = v.split(/;/);
24631             Roo.each(parts, function(p) {
24632                 p = p.replace(/\s+/g,'');
24633                 if (!p.length) {
24634                     return;
24635                 }
24636                 var l = p.split(':').shift().replace(/\s+/g,'');
24637                 
24638                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
24639                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
24640                     node.removeAttribute(n);
24641                     return false;
24642                 }
24643             });
24644             
24645             
24646         }
24647         
24648         
24649         for (var i = node.attributes.length-1; i > -1 ; i--) {
24650             var a = node.attributes[i];
24651             //console.log(a);
24652             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
24653                 node.removeAttribute(a.name);
24654                 return;
24655             }
24656             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
24657                 cleanAttr(a.name,a.value); // fixme..
24658                 return;
24659             }
24660             if (a.name == 'style') {
24661                 cleanStyle(a.name,a.value);
24662             }
24663             /// clean up MS crap..
24664             if (a.name == 'class') {
24665                 if (a.value.match(/^Mso/)) {
24666                     node.className = '';
24667                 }
24668             }
24669             
24670             // style cleanup!?
24671             // class cleanup?
24672             
24673         }
24674         
24675         
24676         this.cleanUpChildren(node);
24677         
24678         
24679     }
24680     
24681     
24682     // hide stuff that is not compatible
24683     /**
24684      * @event blur
24685      * @hide
24686      */
24687     /**
24688      * @event change
24689      * @hide
24690      */
24691     /**
24692      * @event focus
24693      * @hide
24694      */
24695     /**
24696      * @event specialkey
24697      * @hide
24698      */
24699     /**
24700      * @cfg {String} fieldClass @hide
24701      */
24702     /**
24703      * @cfg {String} focusClass @hide
24704      */
24705     /**
24706      * @cfg {String} autoCreate @hide
24707      */
24708     /**
24709      * @cfg {String} inputType @hide
24710      */
24711     /**
24712      * @cfg {String} invalidClass @hide
24713      */
24714     /**
24715      * @cfg {String} invalidText @hide
24716      */
24717     /**
24718      * @cfg {String} msgFx @hide
24719      */
24720     /**
24721      * @cfg {String} validateOnBlur @hide
24722      */
24723 });
24724
24725 Roo.form.HtmlEditor.white = [
24726         'area', 'br', 'img', 'input', 'hr', 'wbr',
24727         
24728        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24729        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24730        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24731        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24732        'table',   'ul',         'xmp', 
24733        
24734        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24735       'thead',   'tr', 
24736      
24737       'dir', 'menu', 'ol', 'ul', 'dl',
24738        
24739       'embed',  'object'
24740 ];
24741
24742
24743 Roo.form.HtmlEditor.black = [
24744     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24745         'applet', // 
24746         'base',   'basefont', 'bgsound', 'blink',  'body', 
24747         'frame',  'frameset', 'head',    'html',   'ilayer', 
24748         'iframe', 'layer',  'link',     'meta',    'object',   
24749         'script', 'style' ,'title',  'xml' // clean later..
24750 ];
24751 Roo.form.HtmlEditor.clean = [
24752     'script', 'style', 'title', 'xml'
24753 ];
24754
24755 // attributes..
24756
24757 Roo.form.HtmlEditor.ablack = [
24758     'on'
24759 ];
24760     
24761 Roo.form.HtmlEditor.aclean = [ 
24762     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24763 ];
24764
24765 // protocols..
24766 Roo.form.HtmlEditor.pwhite= [
24767         'http',  'https',  'mailto'
24768 ];
24769
24770 Roo.form.HtmlEditor.cwhite= [
24771         'text-align',
24772         'font-size'
24773 ];
24774
24775 // <script type="text/javascript">
24776 /*
24777  * Based on
24778  * Ext JS Library 1.1.1
24779  * Copyright(c) 2006-2007, Ext JS, LLC.
24780  *  
24781  
24782  */
24783
24784 /**
24785  * @class Roo.form.HtmlEditorToolbar1
24786  * Basic Toolbar
24787  * 
24788  * Usage:
24789  *
24790  new Roo.form.HtmlEditor({
24791     ....
24792     toolbars : [
24793         new Roo.form.HtmlEditorToolbar1({
24794             disable : { fonts: 1 , format: 1, ..., ... , ...],
24795             btns : [ .... ]
24796         })
24797     }
24798      
24799  * 
24800  * @cfg {Object} disable List of elements to disable..
24801  * @cfg {Array} btns List of additional buttons.
24802  * 
24803  * 
24804  * NEEDS Extra CSS? 
24805  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24806  */
24807  
24808 Roo.form.HtmlEditor.ToolbarStandard = function(config)
24809 {
24810     
24811     Roo.apply(this, config);
24812     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24813     // dont call parent... till later.
24814 }
24815
24816 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
24817     
24818     tb: false,
24819     
24820     rendered: false,
24821     
24822     editor : false,
24823     /**
24824      * @cfg {Object} disable  List of toolbar elements to disable
24825          
24826      */
24827     disable : false,
24828       /**
24829      * @cfg {Array} fontFamilies An array of available font families
24830      */
24831     fontFamilies : [
24832         'Arial',
24833         'Courier New',
24834         'Tahoma',
24835         'Times New Roman',
24836         'Verdana'
24837     ],
24838     
24839     specialChars : [
24840            "&#169;",
24841           "&#174;",     
24842           "&#8482;",    
24843           "&#163;" ,    
24844          // "&#8212;",    
24845           "&#8230;",    
24846           "&#247;" ,    
24847         //  "&#225;" ,     ?? a acute?
24848            "&#8364;"    , //Euro
24849        //   "&#8220;"    ,
24850         //  "&#8221;"    ,
24851         //  "&#8226;"    ,
24852           "&#176;"  //   , // degrees
24853
24854          // "&#233;"     , // e ecute
24855          // "&#250;"     , // u ecute?
24856     ],
24857     inputElements : [ 
24858             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
24859             "input:submit", "input:button", "select", "textarea", "label" ],
24860     formats : [
24861         ["p"] ,  
24862         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
24863         ["pre"],[ "code"], 
24864         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
24865     ],
24866      /**
24867      * @cfg {String} defaultFont default font to use.
24868      */
24869     defaultFont: 'tahoma',
24870    
24871     fontSelect : false,
24872     
24873     
24874     formatCombo : false,
24875     
24876     init : function(editor)
24877     {
24878         this.editor = editor;
24879         
24880         
24881         var fid = editor.frameId;
24882         var etb = this;
24883         function btn(id, toggle, handler){
24884             var xid = fid + '-'+ id ;
24885             return {
24886                 id : xid,
24887                 cmd : id,
24888                 cls : 'x-btn-icon x-edit-'+id,
24889                 enableToggle:toggle !== false,
24890                 scope: editor, // was editor...
24891                 handler:handler||editor.relayBtnCmd,
24892                 clickEvent:'mousedown',
24893                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
24894                 tabIndex:-1
24895             };
24896         }
24897         
24898         
24899         
24900         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
24901         this.tb = tb;
24902          // stop form submits
24903         tb.el.on('click', function(e){
24904             e.preventDefault(); // what does this do?
24905         });
24906
24907         if(!this.disable.font && !Roo.isSafari){
24908             /* why no safari for fonts
24909             editor.fontSelect = tb.el.createChild({
24910                 tag:'select',
24911                 tabIndex: -1,
24912                 cls:'x-font-select',
24913                 html: editor.createFontOptions()
24914             });
24915             editor.fontSelect.on('change', function(){
24916                 var font = editor.fontSelect.dom.value;
24917                 editor.relayCmd('fontname', font);
24918                 editor.deferFocus();
24919             }, editor);
24920             tb.add(
24921                 editor.fontSelect.dom,
24922                 '-'
24923             );
24924             */
24925         };
24926         if(!this.disable.formats){
24927             this.formatCombo = new Roo.form.ComboBox({
24928                 store: new Roo.data.SimpleStore({
24929                     id : 'tag',
24930                     fields: ['tag'],
24931                     data : this.formats // from states.js
24932                 }),
24933                 blockFocus : true,
24934                 //autoCreate : {tag: "div",  size: "20"},
24935                 displayField:'tag',
24936                 typeAhead: false,
24937                 mode: 'local',
24938                 editable : false,
24939                 triggerAction: 'all',
24940                 emptyText:'Add tag',
24941                 selectOnFocus:true,
24942                 width:135,
24943                 listeners : {
24944                     'select': function(c, r, i) {
24945                         editor.insertTag(r.get('tag'));
24946                         editor.focus();
24947                     }
24948                 }
24949
24950             });
24951             tb.addField(this.formatCombo);
24952             
24953         }
24954         
24955         if(!this.disable.format){
24956             tb.add(
24957                 btn('bold'),
24958                 btn('italic'),
24959                 btn('underline')
24960             );
24961         };
24962         if(!this.disable.fontSize){
24963             tb.add(
24964                 '-',
24965                 
24966                 
24967                 btn('increasefontsize', false, editor.adjustFont),
24968                 btn('decreasefontsize', false, editor.adjustFont)
24969             );
24970         };
24971         
24972         
24973         if(this.disable.colors){
24974             tb.add(
24975                 '-', {
24976                     id:editor.frameId +'-forecolor',
24977                     cls:'x-btn-icon x-edit-forecolor',
24978                     clickEvent:'mousedown',
24979                     tooltip: this.buttonTips['forecolor'] || undefined,
24980                     tabIndex:-1,
24981                     menu : new Roo.menu.ColorMenu({
24982                         allowReselect: true,
24983                         focus: Roo.emptyFn,
24984                         value:'000000',
24985                         plain:true,
24986                         selectHandler: function(cp, color){
24987                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
24988                             editor.deferFocus();
24989                         },
24990                         scope: editor,
24991                         clickEvent:'mousedown'
24992                     })
24993                 }, {
24994                     id:editor.frameId +'backcolor',
24995                     cls:'x-btn-icon x-edit-backcolor',
24996                     clickEvent:'mousedown',
24997                     tooltip: this.buttonTips['backcolor'] || undefined,
24998                     tabIndex:-1,
24999                     menu : new Roo.menu.ColorMenu({
25000                         focus: Roo.emptyFn,
25001                         value:'FFFFFF',
25002                         plain:true,
25003                         allowReselect: true,
25004                         selectHandler: function(cp, color){
25005                             if(Roo.isGecko){
25006                                 editor.execCmd('useCSS', false);
25007                                 editor.execCmd('hilitecolor', color);
25008                                 editor.execCmd('useCSS', true);
25009                                 editor.deferFocus();
25010                             }else{
25011                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25012                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25013                                 editor.deferFocus();
25014                             }
25015                         },
25016                         scope:editor,
25017                         clickEvent:'mousedown'
25018                     })
25019                 }
25020             );
25021         };
25022         // now add all the items...
25023         
25024
25025         if(!this.disable.alignments){
25026             tb.add(
25027                 '-',
25028                 btn('justifyleft'),
25029                 btn('justifycenter'),
25030                 btn('justifyright')
25031             );
25032         };
25033
25034         //if(!Roo.isSafari){
25035             if(!this.disable.links){
25036                 tb.add(
25037                     '-',
25038                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25039                 );
25040             };
25041
25042             if(!this.disable.lists){
25043                 tb.add(
25044                     '-',
25045                     btn('insertorderedlist'),
25046                     btn('insertunorderedlist')
25047                 );
25048             }
25049             if(!this.disable.sourceEdit){
25050                 tb.add(
25051                     '-',
25052                     btn('sourceedit', true, function(btn){
25053                         this.toggleSourceEdit(btn.pressed);
25054                     })
25055                 );
25056             }
25057         //}
25058         
25059         var smenu = { };
25060         // special menu.. - needs to be tidied up..
25061         if (!this.disable.special) {
25062             smenu = {
25063                 text: "&#169;",
25064                 cls: 'x-edit-none',
25065                 menu : {
25066                     items : []
25067                    }
25068             };
25069             for (var i =0; i < this.specialChars.length; i++) {
25070                 smenu.menu.items.push({
25071                     
25072                     html: this.specialChars[i],
25073                     handler: function(a,b) {
25074                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25075                         
25076                     },
25077                     tabIndex:-1
25078                 });
25079             }
25080             
25081             
25082             tb.add(smenu);
25083             
25084             
25085         }
25086         if (this.btns) {
25087             for(var i =0; i< this.btns.length;i++) {
25088                 var b = this.btns[i];
25089                 b.cls =  'x-edit-none';
25090                 b.scope = editor;
25091                 tb.add(b);
25092             }
25093         
25094         }
25095         
25096         
25097         
25098         // disable everything...
25099         
25100         this.tb.items.each(function(item){
25101            if(item.id != editor.frameId+ '-sourceedit'){
25102                 item.disable();
25103             }
25104         });
25105         this.rendered = true;
25106         
25107         // the all the btns;
25108         editor.on('editorevent', this.updateToolbar, this);
25109         // other toolbars need to implement this..
25110         //editor.on('editmodechange', this.updateToolbar, this);
25111     },
25112     
25113     
25114     
25115     /**
25116      * Protected method that will not generally be called directly. It triggers
25117      * a toolbar update by reading the markup state of the current selection in the editor.
25118      */
25119     updateToolbar: function(){
25120
25121         if(!this.editor.activated){
25122             this.editor.onFirstFocus();
25123             return;
25124         }
25125
25126         var btns = this.tb.items.map, 
25127             doc = this.editor.doc,
25128             frameId = this.editor.frameId;
25129
25130         if(!this.disable.font && !Roo.isSafari){
25131             /*
25132             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25133             if(name != this.fontSelect.dom.value){
25134                 this.fontSelect.dom.value = name;
25135             }
25136             */
25137         }
25138         if(!this.disable.format){
25139             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25140             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25141             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25142         }
25143         if(!this.disable.alignments){
25144             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25145             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25146             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25147         }
25148         if(!Roo.isSafari && !this.disable.lists){
25149             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25150             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25151         }
25152         
25153         var ans = this.editor.getAllAncestors();
25154         if (this.formatCombo) {
25155             
25156             
25157             var store = this.formatCombo.store;
25158             this.formatCombo.setValue("");
25159             for (var i =0; i < ans.length;i++) {
25160                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25161                     // select it..
25162                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25163                     break;
25164                 }
25165             }
25166         }
25167         
25168         
25169         
25170         // hides menus... - so this cant be on a menu...
25171         Roo.menu.MenuMgr.hideAll();
25172
25173         //this.editorsyncValue();
25174     },
25175    
25176     
25177     createFontOptions : function(){
25178         var buf = [], fs = this.fontFamilies, ff, lc;
25179         for(var i = 0, len = fs.length; i< len; i++){
25180             ff = fs[i];
25181             lc = ff.toLowerCase();
25182             buf.push(
25183                 '<option value="',lc,'" style="font-family:',ff,';"',
25184                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25185                     ff,
25186                 '</option>'
25187             );
25188         }
25189         return buf.join('');
25190     },
25191     
25192     toggleSourceEdit : function(sourceEditMode){
25193         if(sourceEditMode === undefined){
25194             sourceEditMode = !this.sourceEditMode;
25195         }
25196         this.sourceEditMode = sourceEditMode === true;
25197         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25198         // just toggle the button?
25199         if(btn.pressed !== this.editor.sourceEditMode){
25200             btn.toggle(this.editor.sourceEditMode);
25201             return;
25202         }
25203         
25204         if(this.sourceEditMode){
25205             this.tb.items.each(function(item){
25206                 if(item.cmd != 'sourceedit'){
25207                     item.disable();
25208                 }
25209             });
25210           
25211         }else{
25212             if(this.initialized){
25213                 this.tb.items.each(function(item){
25214                     item.enable();
25215                 });
25216             }
25217             
25218         }
25219         // tell the editor that it's been pressed..
25220         this.editor.toggleSourceEdit(sourceEditMode);
25221        
25222     },
25223      /**
25224      * Object collection of toolbar tooltips for the buttons in the editor. The key
25225      * is the command id associated with that button and the value is a valid QuickTips object.
25226      * For example:
25227 <pre><code>
25228 {
25229     bold : {
25230         title: 'Bold (Ctrl+B)',
25231         text: 'Make the selected text bold.',
25232         cls: 'x-html-editor-tip'
25233     },
25234     italic : {
25235         title: 'Italic (Ctrl+I)',
25236         text: 'Make the selected text italic.',
25237         cls: 'x-html-editor-tip'
25238     },
25239     ...
25240 </code></pre>
25241     * @type Object
25242      */
25243     buttonTips : {
25244         bold : {
25245             title: 'Bold (Ctrl+B)',
25246             text: 'Make the selected text bold.',
25247             cls: 'x-html-editor-tip'
25248         },
25249         italic : {
25250             title: 'Italic (Ctrl+I)',
25251             text: 'Make the selected text italic.',
25252             cls: 'x-html-editor-tip'
25253         },
25254         underline : {
25255             title: 'Underline (Ctrl+U)',
25256             text: 'Underline the selected text.',
25257             cls: 'x-html-editor-tip'
25258         },
25259         increasefontsize : {
25260             title: 'Grow Text',
25261             text: 'Increase the font size.',
25262             cls: 'x-html-editor-tip'
25263         },
25264         decreasefontsize : {
25265             title: 'Shrink Text',
25266             text: 'Decrease the font size.',
25267             cls: 'x-html-editor-tip'
25268         },
25269         backcolor : {
25270             title: 'Text Highlight Color',
25271             text: 'Change the background color of the selected text.',
25272             cls: 'x-html-editor-tip'
25273         },
25274         forecolor : {
25275             title: 'Font Color',
25276             text: 'Change the color of the selected text.',
25277             cls: 'x-html-editor-tip'
25278         },
25279         justifyleft : {
25280             title: 'Align Text Left',
25281             text: 'Align text to the left.',
25282             cls: 'x-html-editor-tip'
25283         },
25284         justifycenter : {
25285             title: 'Center Text',
25286             text: 'Center text in the editor.',
25287             cls: 'x-html-editor-tip'
25288         },
25289         justifyright : {
25290             title: 'Align Text Right',
25291             text: 'Align text to the right.',
25292             cls: 'x-html-editor-tip'
25293         },
25294         insertunorderedlist : {
25295             title: 'Bullet List',
25296             text: 'Start a bulleted list.',
25297             cls: 'x-html-editor-tip'
25298         },
25299         insertorderedlist : {
25300             title: 'Numbered List',
25301             text: 'Start a numbered list.',
25302             cls: 'x-html-editor-tip'
25303         },
25304         createlink : {
25305             title: 'Hyperlink',
25306             text: 'Make the selected text a hyperlink.',
25307             cls: 'x-html-editor-tip'
25308         },
25309         sourceedit : {
25310             title: 'Source Edit',
25311             text: 'Switch to source editing mode.',
25312             cls: 'x-html-editor-tip'
25313         }
25314     },
25315     // private
25316     onDestroy : function(){
25317         if(this.rendered){
25318             
25319             this.tb.items.each(function(item){
25320                 if(item.menu){
25321                     item.menu.removeAll();
25322                     if(item.menu.el){
25323                         item.menu.el.destroy();
25324                     }
25325                 }
25326                 item.destroy();
25327             });
25328              
25329         }
25330     },
25331     onFirstFocus: function() {
25332         this.tb.items.each(function(item){
25333            item.enable();
25334         });
25335     }
25336 });
25337
25338
25339
25340