roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         Event.on(this.id, "mousedown", this.handleMouseDown, this);
569         // Event.on(this.id, "selectstart", Event.preventDefault);
570     },
571
572     /**
573      * Initializes Targeting functionality only... the object does not
574      * get a mousedown handler.
575      * @method initTarget
576      * @param id the id of the linked element
577      * @param {String} sGroup the group of related items
578      * @param {object} config configuration attributes
579      */
580     initTarget: function(id, sGroup, config) {
581
582         // configuration attributes
583         this.config = config || {};
584
585         // create a local reference to the drag and drop manager
586         this.DDM = Roo.dd.DDM;
587         // initialize the groups array
588         this.groups = {};
589
590         // assume that we have an element reference instead of an id if the
591         // parameter is not a string
592         if (typeof id !== "string") {
593             id = Roo.id(id);
594         }
595
596         // set the id
597         this.id = id;
598
599         // add to an interaction group
600         this.addToGroup((sGroup) ? sGroup : "default");
601
602         // We don't want to register this as the handle with the manager
603         // so we just set the id rather than calling the setter.
604         this.handleElId = id;
605
606         // the linked element is the element that gets dragged by default
607         this.setDragElId(id);
608
609         // by default, clicked anchors will not start drag operations.
610         this.invalidHandleTypes = { A: "A" };
611         this.invalidHandleIds = {};
612         this.invalidHandleClasses = [];
613
614         this.applyConfig();
615
616         this.handleOnAvailable();
617     },
618
619     /**
620      * Applies the configuration parameters that were passed into the constructor.
621      * This is supposed to happen at each level through the inheritance chain.  So
622      * a DDProxy implentation will execute apply config on DDProxy, DD, and
623      * DragDrop in order to get all of the parameters that are available in
624      * each object.
625      * @method applyConfig
626      */
627     applyConfig: function() {
628
629         // configurable properties:
630         //    padding, isTarget, maintainOffset, primaryButtonOnly
631         this.padding           = this.config.padding || [0, 0, 0, 0];
632         this.isTarget          = (this.config.isTarget !== false);
633         this.maintainOffset    = (this.config.maintainOffset);
634         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
635
636     },
637
638     /**
639      * Executed when the linked element is available
640      * @method handleOnAvailable
641      * @private
642      */
643     handleOnAvailable: function() {
644         this.available = true;
645         this.resetConstraints();
646         this.onAvailable();
647     },
648
649      /**
650      * Configures the padding for the target zone in px.  Effectively expands
651      * (or reduces) the virtual object size for targeting calculations.
652      * Supports css-style shorthand; if only one parameter is passed, all sides
653      * will have that padding, and if only two are passed, the top and bottom
654      * will have the first param, the left and right the second.
655      * @method setPadding
656      * @param {int} iTop    Top pad
657      * @param {int} iRight  Right pad
658      * @param {int} iBot    Bot pad
659      * @param {int} iLeft   Left pad
660      */
661     setPadding: function(iTop, iRight, iBot, iLeft) {
662         // this.padding = [iLeft, iRight, iTop, iBot];
663         if (!iRight && 0 !== iRight) {
664             this.padding = [iTop, iTop, iTop, iTop];
665         } else if (!iBot && 0 !== iBot) {
666             this.padding = [iTop, iRight, iTop, iRight];
667         } else {
668             this.padding = [iTop, iRight, iBot, iLeft];
669         }
670     },
671
672     /**
673      * Stores the initial placement of the linked element.
674      * @method setInitialPosition
675      * @param {int} diffX   the X offset, default 0
676      * @param {int} diffY   the Y offset, default 0
677      */
678     setInitPosition: function(diffX, diffY) {
679         var el = this.getEl();
680
681         if (!this.DDM.verifyEl(el)) {
682             return;
683         }
684
685         var dx = diffX || 0;
686         var dy = diffY || 0;
687
688         var p = Dom.getXY( el );
689
690         this.initPageX = p[0] - dx;
691         this.initPageY = p[1] - dy;
692
693         this.lastPageX = p[0];
694         this.lastPageY = p[1];
695
696
697         this.setStartPosition(p);
698     },
699
700     /**
701      * Sets the start position of the element.  This is set when the obj
702      * is initialized, the reset when a drag is started.
703      * @method setStartPosition
704      * @param pos current position (from previous lookup)
705      * @private
706      */
707     setStartPosition: function(pos) {
708         var p = pos || Dom.getXY( this.getEl() );
709         this.deltaSetXY = null;
710
711         this.startPageX = p[0];
712         this.startPageY = p[1];
713     },
714
715     /**
716      * Add this instance to a group of related drag/drop objects.  All
717      * instances belong to at least one group, and can belong to as many
718      * groups as needed.
719      * @method addToGroup
720      * @param sGroup {string} the name of the group
721      */
722     addToGroup: function(sGroup) {
723         this.groups[sGroup] = true;
724         this.DDM.regDragDrop(this, sGroup);
725     },
726
727     /**
728      * Remove's this instance from the supplied interaction group
729      * @method removeFromGroup
730      * @param {string}  sGroup  The group to drop
731      */
732     removeFromGroup: function(sGroup) {
733         if (this.groups[sGroup]) {
734             delete this.groups[sGroup];
735         }
736
737         this.DDM.removeDDFromGroup(this, sGroup);
738     },
739
740     /**
741      * Allows you to specify that an element other than the linked element
742      * will be moved with the cursor during a drag
743      * @method setDragElId
744      * @param id {string} the id of the element that will be used to initiate the drag
745      */
746     setDragElId: function(id) {
747         this.dragElId = id;
748     },
749
750     /**
751      * Allows you to specify a child of the linked element that should be
752      * used to initiate the drag operation.  An example of this would be if
753      * you have a content div with text and links.  Clicking anywhere in the
754      * content area would normally start the drag operation.  Use this method
755      * to specify that an element inside of the content div is the element
756      * that starts the drag operation.
757      * @method setHandleElId
758      * @param id {string} the id of the element that will be used to
759      * initiate the drag.
760      */
761     setHandleElId: function(id) {
762         if (typeof id !== "string") {
763             id = Roo.id(id);
764         }
765         this.handleElId = id;
766         this.DDM.regHandle(this.id, id);
767     },
768
769     /**
770      * Allows you to set an element outside of the linked element as a drag
771      * handle
772      * @method setOuterHandleElId
773      * @param id the id of the element that will be used to initiate the drag
774      */
775     setOuterHandleElId: function(id) {
776         if (typeof id !== "string") {
777             id = Roo.id(id);
778         }
779         Event.on(id, "mousedown",
780                 this.handleMouseDown, this);
781         this.setHandleElId(id);
782
783         this.hasOuterHandles = true;
784     },
785
786     /**
787      * Remove all drag and drop hooks for this element
788      * @method unreg
789      */
790     unreg: function() {
791         Event.un(this.id, "mousedown",
792                 this.handleMouseDown);
793         this._domRef = null;
794         this.DDM._remove(this);
795     },
796
797     destroy : function(){
798         this.unreg();
799     },
800
801     /**
802      * Returns true if this instance is locked, or the drag drop mgr is locked
803      * (meaning that all drag/drop is disabled on the page.)
804      * @method isLocked
805      * @return {boolean} true if this obj or all drag/drop is locked, else
806      * false
807      */
808     isLocked: function() {
809         return (this.DDM.isLocked() || this.locked);
810     },
811
812     /**
813      * Fired when this object is clicked
814      * @method handleMouseDown
815      * @param {Event} e
816      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
817      * @private
818      */
819     handleMouseDown: function(e, oDD){
820         if (this.primaryButtonOnly && e.button != 0) {
821             return;
822         }
823
824         if (this.isLocked()) {
825             return;
826         }
827
828         this.DDM.refreshCache(this.groups);
829
830         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
831         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
832         } else {
833             if (this.clickValidator(e)) {
834
835                 // set the initial element position
836                 this.setStartPosition();
837
838
839                 this.b4MouseDown(e);
840                 this.onMouseDown(e);
841
842                 this.DDM.handleMouseDown(e, this);
843
844                 this.DDM.stopEvent(e);
845             } else {
846
847
848             }
849         }
850     },
851
852     clickValidator: function(e) {
853         var target = e.getTarget();
854         return ( this.isValidHandleChild(target) &&
855                     (this.id == this.handleElId ||
856                         this.DDM.handleWasClicked(target, this.id)) );
857     },
858
859     /**
860      * Allows you to specify a tag name that should not start a drag operation
861      * when clicked.  This is designed to facilitate embedding links within a
862      * drag handle that do something other than start the drag.
863      * @method addInvalidHandleType
864      * @param {string} tagName the type of element to exclude
865      */
866     addInvalidHandleType: function(tagName) {
867         var type = tagName.toUpperCase();
868         this.invalidHandleTypes[type] = type;
869     },
870
871     /**
872      * Lets you to specify an element id for a child of a drag handle
873      * that should not initiate a drag
874      * @method addInvalidHandleId
875      * @param {string} id the element id of the element you wish to ignore
876      */
877     addInvalidHandleId: function(id) {
878         if (typeof id !== "string") {
879             id = Roo.id(id);
880         }
881         this.invalidHandleIds[id] = id;
882     },
883
884     /**
885      * Lets you specify a css class of elements that will not initiate a drag
886      * @method addInvalidHandleClass
887      * @param {string} cssClass the class of the elements you wish to ignore
888      */
889     addInvalidHandleClass: function(cssClass) {
890         this.invalidHandleClasses.push(cssClass);
891     },
892
893     /**
894      * Unsets an excluded tag name set by addInvalidHandleType
895      * @method removeInvalidHandleType
896      * @param {string} tagName the type of element to unexclude
897      */
898     removeInvalidHandleType: function(tagName) {
899         var type = tagName.toUpperCase();
900         // this.invalidHandleTypes[type] = null;
901         delete this.invalidHandleTypes[type];
902     },
903
904     /**
905      * Unsets an invalid handle id
906      * @method removeInvalidHandleId
907      * @param {string} id the id of the element to re-enable
908      */
909     removeInvalidHandleId: function(id) {
910         if (typeof id !== "string") {
911             id = Roo.id(id);
912         }
913         delete this.invalidHandleIds[id];
914     },
915
916     /**
917      * Unsets an invalid css class
918      * @method removeInvalidHandleClass
919      * @param {string} cssClass the class of the element(s) you wish to
920      * re-enable
921      */
922     removeInvalidHandleClass: function(cssClass) {
923         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
924             if (this.invalidHandleClasses[i] == cssClass) {
925                 delete this.invalidHandleClasses[i];
926             }
927         }
928     },
929
930     /**
931      * Checks the tag exclusion list to see if this click should be ignored
932      * @method isValidHandleChild
933      * @param {HTMLElement} node the HTMLElement to evaluate
934      * @return {boolean} true if this is a valid tag type, false if not
935      */
936     isValidHandleChild: function(node) {
937
938         var valid = true;
939         // var n = (node.nodeName == "#text") ? node.parentNode : node;
940         var nodeName;
941         try {
942             nodeName = node.nodeName.toUpperCase();
943         } catch(e) {
944             nodeName = node.nodeName;
945         }
946         valid = valid && !this.invalidHandleTypes[nodeName];
947         valid = valid && !this.invalidHandleIds[node.id];
948
949         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
950             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
951         }
952
953
954         return valid;
955
956     },
957
958     /**
959      * Create the array of horizontal tick marks if an interval was specified
960      * in setXConstraint().
961      * @method setXTicks
962      * @private
963      */
964     setXTicks: function(iStartX, iTickSize) {
965         this.xTicks = [];
966         this.xTickSize = iTickSize;
967
968         var tickMap = {};
969
970         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
971             if (!tickMap[i]) {
972                 this.xTicks[this.xTicks.length] = i;
973                 tickMap[i] = true;
974             }
975         }
976
977         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
978             if (!tickMap[i]) {
979                 this.xTicks[this.xTicks.length] = i;
980                 tickMap[i] = true;
981             }
982         }
983
984         this.xTicks.sort(this.DDM.numericSort) ;
985     },
986
987     /**
988      * Create the array of vertical tick marks if an interval was specified in
989      * setYConstraint().
990      * @method setYTicks
991      * @private
992      */
993     setYTicks: function(iStartY, iTickSize) {
994         this.yTicks = [];
995         this.yTickSize = iTickSize;
996
997         var tickMap = {};
998
999         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1000             if (!tickMap[i]) {
1001                 this.yTicks[this.yTicks.length] = i;
1002                 tickMap[i] = true;
1003             }
1004         }
1005
1006         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1007             if (!tickMap[i]) {
1008                 this.yTicks[this.yTicks.length] = i;
1009                 tickMap[i] = true;
1010             }
1011         }
1012
1013         this.yTicks.sort(this.DDM.numericSort) ;
1014     },
1015
1016     /**
1017      * By default, the element can be dragged any place on the screen.  Use
1018      * this method to limit the horizontal travel of the element.  Pass in
1019      * 0,0 for the parameters if you want to lock the drag to the y axis.
1020      * @method setXConstraint
1021      * @param {int} iLeft the number of pixels the element can move to the left
1022      * @param {int} iRight the number of pixels the element can move to the
1023      * right
1024      * @param {int} iTickSize optional parameter for specifying that the
1025      * element
1026      * should move iTickSize pixels at a time.
1027      */
1028     setXConstraint: function(iLeft, iRight, iTickSize) {
1029         this.leftConstraint = iLeft;
1030         this.rightConstraint = iRight;
1031
1032         this.minX = this.initPageX - iLeft;
1033         this.maxX = this.initPageX + iRight;
1034         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1035
1036         this.constrainX = true;
1037     },
1038
1039     /**
1040      * Clears any constraints applied to this instance.  Also clears ticks
1041      * since they can't exist independent of a constraint at this time.
1042      * @method clearConstraints
1043      */
1044     clearConstraints: function() {
1045         this.constrainX = false;
1046         this.constrainY = false;
1047         this.clearTicks();
1048     },
1049
1050     /**
1051      * Clears any tick interval defined for this instance
1052      * @method clearTicks
1053      */
1054     clearTicks: function() {
1055         this.xTicks = null;
1056         this.yTicks = null;
1057         this.xTickSize = 0;
1058         this.yTickSize = 0;
1059     },
1060
1061     /**
1062      * By default, the element can be dragged any place on the screen.  Set
1063      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1064      * parameters if you want to lock the drag to the x axis.
1065      * @method setYConstraint
1066      * @param {int} iUp the number of pixels the element can move up
1067      * @param {int} iDown the number of pixels the element can move down
1068      * @param {int} iTickSize optional parameter for specifying that the
1069      * element should move iTickSize pixels at a time.
1070      */
1071     setYConstraint: function(iUp, iDown, iTickSize) {
1072         this.topConstraint = iUp;
1073         this.bottomConstraint = iDown;
1074
1075         this.minY = this.initPageY - iUp;
1076         this.maxY = this.initPageY + iDown;
1077         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1078
1079         this.constrainY = true;
1080
1081     },
1082
1083     /**
1084      * resetConstraints must be called if you manually reposition a dd element.
1085      * @method resetConstraints
1086      * @param {boolean} maintainOffset
1087      */
1088     resetConstraints: function() {
1089
1090
1091         // Maintain offsets if necessary
1092         if (this.initPageX || this.initPageX === 0) {
1093             // figure out how much this thing has moved
1094             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1095             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1096
1097             this.setInitPosition(dx, dy);
1098
1099         // This is the first time we have detected the element's position
1100         } else {
1101             this.setInitPosition();
1102         }
1103
1104         if (this.constrainX) {
1105             this.setXConstraint( this.leftConstraint,
1106                                  this.rightConstraint,
1107                                  this.xTickSize        );
1108         }
1109
1110         if (this.constrainY) {
1111             this.setYConstraint( this.topConstraint,
1112                                  this.bottomConstraint,
1113                                  this.yTickSize         );
1114         }
1115     },
1116
1117     /**
1118      * Normally the drag element is moved pixel by pixel, but we can specify
1119      * that it move a number of pixels at a time.  This method resolves the
1120      * location when we have it set up like this.
1121      * @method getTick
1122      * @param {int} val where we want to place the object
1123      * @param {int[]} tickArray sorted array of valid points
1124      * @return {int} the closest tick
1125      * @private
1126      */
1127     getTick: function(val, tickArray) {
1128
1129         if (!tickArray) {
1130             // If tick interval is not defined, it is effectively 1 pixel,
1131             // so we return the value passed to us.
1132             return val;
1133         } else if (tickArray[0] >= val) {
1134             // The value is lower than the first tick, so we return the first
1135             // tick.
1136             return tickArray[0];
1137         } else {
1138             for (var i=0, len=tickArray.length; i<len; ++i) {
1139                 var next = i + 1;
1140                 if (tickArray[next] && tickArray[next] >= val) {
1141                     var diff1 = val - tickArray[i];
1142                     var diff2 = tickArray[next] - val;
1143                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1144                 }
1145             }
1146
1147             // The value is larger than the last tick, so we return the last
1148             // tick.
1149             return tickArray[tickArray.length - 1];
1150         }
1151     },
1152
1153     /**
1154      * toString method
1155      * @method toString
1156      * @return {string} string representation of the dd obj
1157      */
1158     toString: function() {
1159         return ("DragDrop " + this.id);
1160     }
1161
1162 });
1163
1164 })();
1165 /*
1166  * Based on:
1167  * Ext JS Library 1.1.1
1168  * Copyright(c) 2006-2007, Ext JS, LLC.
1169  *
1170  * Originally Released Under LGPL - original licence link has changed is not relivant.
1171  *
1172  * Fork - LGPL
1173  * <script type="text/javascript">
1174  */
1175
1176
1177 /**
1178  * The drag and drop utility provides a framework for building drag and drop
1179  * applications.  In addition to enabling drag and drop for specific elements,
1180  * the drag and drop elements are tracked by the manager class, and the
1181  * interactions between the various elements are tracked during the drag and
1182  * the implementing code is notified about these important moments.
1183  */
1184
1185 // Only load the library once.  Rewriting the manager class would orphan
1186 // existing drag and drop instances.
1187 if (!Roo.dd.DragDropMgr) {
1188
1189 /**
1190  * @class Roo.dd.DragDropMgr
1191  * DragDropMgr is a singleton that tracks the element interaction for
1192  * all DragDrop items in the window.  Generally, you will not call
1193  * this class directly, but it does have helper methods that could
1194  * be useful in your DragDrop implementations.
1195  * @singleton
1196  */
1197 Roo.dd.DragDropMgr = function() {
1198
1199     var Event = Roo.EventManager;
1200
1201     return {
1202
1203         /**
1204          * Two dimensional Array of registered DragDrop objects.  The first
1205          * dimension is the DragDrop item group, the second the DragDrop
1206          * object.
1207          * @property ids
1208          * @type {string: string}
1209          * @private
1210          * @static
1211          */
1212         ids: {},
1213
1214         /**
1215          * Array of element ids defined as drag handles.  Used to determine
1216          * if the element that generated the mousedown event is actually the
1217          * handle and not the html element itself.
1218          * @property handleIds
1219          * @type {string: string}
1220          * @private
1221          * @static
1222          */
1223         handleIds: {},
1224
1225         /**
1226          * the DragDrop object that is currently being dragged
1227          * @property dragCurrent
1228          * @type DragDrop
1229          * @private
1230          * @static
1231          **/
1232         dragCurrent: null,
1233
1234         /**
1235          * the DragDrop object(s) that are being hovered over
1236          * @property dragOvers
1237          * @type Array
1238          * @private
1239          * @static
1240          */
1241         dragOvers: {},
1242
1243         /**
1244          * the X distance between the cursor and the object being dragged
1245          * @property deltaX
1246          * @type int
1247          * @private
1248          * @static
1249          */
1250         deltaX: 0,
1251
1252         /**
1253          * the Y distance between the cursor and the object being dragged
1254          * @property deltaY
1255          * @type int
1256          * @private
1257          * @static
1258          */
1259         deltaY: 0,
1260
1261         /**
1262          * Flag to determine if we should prevent the default behavior of the
1263          * events we define. By default this is true, but this can be set to
1264          * false if you need the default behavior (not recommended)
1265          * @property preventDefault
1266          * @type boolean
1267          * @static
1268          */
1269         preventDefault: true,
1270
1271         /**
1272          * Flag to determine if we should stop the propagation of the events
1273          * we generate. This is true by default but you may want to set it to
1274          * false if the html element contains other features that require the
1275          * mouse click.
1276          * @property stopPropagation
1277          * @type boolean
1278          * @static
1279          */
1280         stopPropagation: true,
1281
1282         /**
1283          * Internal flag that is set to true when drag and drop has been
1284          * intialized
1285          * @property initialized
1286          * @private
1287          * @static
1288          */
1289         initalized: false,
1290
1291         /**
1292          * All drag and drop can be disabled.
1293          * @property locked
1294          * @private
1295          * @static
1296          */
1297         locked: false,
1298
1299         /**
1300          * Called the first time an element is registered.
1301          * @method init
1302          * @private
1303          * @static
1304          */
1305         init: function() {
1306             this.initialized = true;
1307         },
1308
1309         /**
1310          * In point mode, drag and drop interaction is defined by the
1311          * location of the cursor during the drag/drop
1312          * @property POINT
1313          * @type int
1314          * @static
1315          */
1316         POINT: 0,
1317
1318         /**
1319          * In intersect mode, drag and drop interactio nis defined by the
1320          * overlap of two or more drag and drop objects.
1321          * @property INTERSECT
1322          * @type int
1323          * @static
1324          */
1325         INTERSECT: 1,
1326
1327         /**
1328          * The current drag and drop mode.  Default: POINT
1329          * @property mode
1330          * @type int
1331          * @static
1332          */
1333         mode: 0,
1334
1335         /**
1336          * Runs method on all drag and drop objects
1337          * @method _execOnAll
1338          * @private
1339          * @static
1340          */
1341         _execOnAll: function(sMethod, args) {
1342             for (var i in this.ids) {
1343                 for (var j in this.ids[i]) {
1344                     var oDD = this.ids[i][j];
1345                     if (! this.isTypeOfDD(oDD)) {
1346                         continue;
1347                     }
1348                     oDD[sMethod].apply(oDD, args);
1349                 }
1350             }
1351         },
1352
1353         /**
1354          * Drag and drop initialization.  Sets up the global event handlers
1355          * @method _onLoad
1356          * @private
1357          * @static
1358          */
1359         _onLoad: function() {
1360
1361             this.init();
1362
1363
1364             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1365             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1366             Event.on(window,   "unload",    this._onUnload, this, true);
1367             Event.on(window,   "resize",    this._onResize, this, true);
1368             // Event.on(window,   "mouseout",    this._test);
1369
1370         },
1371
1372         /**
1373          * Reset constraints on all drag and drop objs
1374          * @method _onResize
1375          * @private
1376          * @static
1377          */
1378         _onResize: function(e) {
1379             this._execOnAll("resetConstraints", []);
1380         },
1381
1382         /**
1383          * Lock all drag and drop functionality
1384          * @method lock
1385          * @static
1386          */
1387         lock: function() { this.locked = true; },
1388
1389         /**
1390          * Unlock all drag and drop functionality
1391          * @method unlock
1392          * @static
1393          */
1394         unlock: function() { this.locked = false; },
1395
1396         /**
1397          * Is drag and drop locked?
1398          * @method isLocked
1399          * @return {boolean} True if drag and drop is locked, false otherwise.
1400          * @static
1401          */
1402         isLocked: function() { return this.locked; },
1403
1404         /**
1405          * Location cache that is set for all drag drop objects when a drag is
1406          * initiated, cleared when the drag is finished.
1407          * @property locationCache
1408          * @private
1409          * @static
1410          */
1411         locationCache: {},
1412
1413         /**
1414          * Set useCache to false if you want to force object the lookup of each
1415          * drag and drop linked element constantly during a drag.
1416          * @property useCache
1417          * @type boolean
1418          * @static
1419          */
1420         useCache: true,
1421
1422         /**
1423          * The number of pixels that the mouse needs to move after the
1424          * mousedown before the drag is initiated.  Default=3;
1425          * @property clickPixelThresh
1426          * @type int
1427          * @static
1428          */
1429         clickPixelThresh: 3,
1430
1431         /**
1432          * The number of milliseconds after the mousedown event to initiate the
1433          * drag if we don't get a mouseup event. Default=1000
1434          * @property clickTimeThresh
1435          * @type int
1436          * @static
1437          */
1438         clickTimeThresh: 350,
1439
1440         /**
1441          * Flag that indicates that either the drag pixel threshold or the
1442          * mousdown time threshold has been met
1443          * @property dragThreshMet
1444          * @type boolean
1445          * @private
1446          * @static
1447          */
1448         dragThreshMet: false,
1449
1450         /**
1451          * Timeout used for the click time threshold
1452          * @property clickTimeout
1453          * @type Object
1454          * @private
1455          * @static
1456          */
1457         clickTimeout: null,
1458
1459         /**
1460          * The X position of the mousedown event stored for later use when a
1461          * drag threshold is met.
1462          * @property startX
1463          * @type int
1464          * @private
1465          * @static
1466          */
1467         startX: 0,
1468
1469         /**
1470          * The Y position of the mousedown event stored for later use when a
1471          * drag threshold is met.
1472          * @property startY
1473          * @type int
1474          * @private
1475          * @static
1476          */
1477         startY: 0,
1478
1479         /**
1480          * Each DragDrop instance must be registered with the DragDropMgr.
1481          * This is executed in DragDrop.init()
1482          * @method regDragDrop
1483          * @param {DragDrop} oDD the DragDrop object to register
1484          * @param {String} sGroup the name of the group this element belongs to
1485          * @static
1486          */
1487         regDragDrop: function(oDD, sGroup) {
1488             if (!this.initialized) { this.init(); }
1489
1490             if (!this.ids[sGroup]) {
1491                 this.ids[sGroup] = {};
1492             }
1493             this.ids[sGroup][oDD.id] = oDD;
1494         },
1495
1496         /**
1497          * Removes the supplied dd instance from the supplied group. Executed
1498          * by DragDrop.removeFromGroup, so don't call this function directly.
1499          * @method removeDDFromGroup
1500          * @private
1501          * @static
1502          */
1503         removeDDFromGroup: function(oDD, sGroup) {
1504             if (!this.ids[sGroup]) {
1505                 this.ids[sGroup] = {};
1506             }
1507
1508             var obj = this.ids[sGroup];
1509             if (obj && obj[oDD.id]) {
1510                 delete obj[oDD.id];
1511             }
1512         },
1513
1514         /**
1515          * Unregisters a drag and drop item.  This is executed in
1516          * DragDrop.unreg, use that method instead of calling this directly.
1517          * @method _remove
1518          * @private
1519          * @static
1520          */
1521         _remove: function(oDD) {
1522             for (var g in oDD.groups) {
1523                 if (g && this.ids[g][oDD.id]) {
1524                     delete this.ids[g][oDD.id];
1525                 }
1526             }
1527             delete this.handleIds[oDD.id];
1528         },
1529
1530         /**
1531          * Each DragDrop handle element must be registered.  This is done
1532          * automatically when executing DragDrop.setHandleElId()
1533          * @method regHandle
1534          * @param {String} sDDId the DragDrop id this element is a handle for
1535          * @param {String} sHandleId the id of the element that is the drag
1536          * handle
1537          * @static
1538          */
1539         regHandle: function(sDDId, sHandleId) {
1540             if (!this.handleIds[sDDId]) {
1541                 this.handleIds[sDDId] = {};
1542             }
1543             this.handleIds[sDDId][sHandleId] = sHandleId;
1544         },
1545
1546         /**
1547          * Utility function to determine if a given element has been
1548          * registered as a drag drop item.
1549          * @method isDragDrop
1550          * @param {String} id the element id to check
1551          * @return {boolean} true if this element is a DragDrop item,
1552          * false otherwise
1553          * @static
1554          */
1555         isDragDrop: function(id) {
1556             return ( this.getDDById(id) ) ? true : false;
1557         },
1558
1559         /**
1560          * Returns the drag and drop instances that are in all groups the
1561          * passed in instance belongs to.
1562          * @method getRelated
1563          * @param {DragDrop} p_oDD the obj to get related data for
1564          * @param {boolean} bTargetsOnly if true, only return targetable objs
1565          * @return {DragDrop[]} the related instances
1566          * @static
1567          */
1568         getRelated: function(p_oDD, bTargetsOnly) {
1569             var oDDs = [];
1570             for (var i in p_oDD.groups) {
1571                 for (j in this.ids[i]) {
1572                     var dd = this.ids[i][j];
1573                     if (! this.isTypeOfDD(dd)) {
1574                         continue;
1575                     }
1576                     if (!bTargetsOnly || dd.isTarget) {
1577                         oDDs[oDDs.length] = dd;
1578                     }
1579                 }
1580             }
1581
1582             return oDDs;
1583         },
1584
1585         /**
1586          * Returns true if the specified dd target is a legal target for
1587          * the specifice drag obj
1588          * @method isLegalTarget
1589          * @param {DragDrop} the drag obj
1590          * @param {DragDrop} the target
1591          * @return {boolean} true if the target is a legal target for the
1592          * dd obj
1593          * @static
1594          */
1595         isLegalTarget: function (oDD, oTargetDD) {
1596             var targets = this.getRelated(oDD, true);
1597             for (var i=0, len=targets.length;i<len;++i) {
1598                 if (targets[i].id == oTargetDD.id) {
1599                     return true;
1600                 }
1601             }
1602
1603             return false;
1604         },
1605
1606         /**
1607          * My goal is to be able to transparently determine if an object is
1608          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1609          * returns "object", oDD.constructor.toString() always returns
1610          * "DragDrop" and not the name of the subclass.  So for now it just
1611          * evaluates a well-known variable in DragDrop.
1612          * @method isTypeOfDD
1613          * @param {Object} the object to evaluate
1614          * @return {boolean} true if typeof oDD = DragDrop
1615          * @static
1616          */
1617         isTypeOfDD: function (oDD) {
1618             return (oDD && oDD.__ygDragDrop);
1619         },
1620
1621         /**
1622          * Utility function to determine if a given element has been
1623          * registered as a drag drop handle for the given Drag Drop object.
1624          * @method isHandle
1625          * @param {String} id the element id to check
1626          * @return {boolean} true if this element is a DragDrop handle, false
1627          * otherwise
1628          * @static
1629          */
1630         isHandle: function(sDDId, sHandleId) {
1631             return ( this.handleIds[sDDId] &&
1632                             this.handleIds[sDDId][sHandleId] );
1633         },
1634
1635         /**
1636          * Returns the DragDrop instance for a given id
1637          * @method getDDById
1638          * @param {String} id the id of the DragDrop object
1639          * @return {DragDrop} the drag drop object, null if it is not found
1640          * @static
1641          */
1642         getDDById: function(id) {
1643             for (var i in this.ids) {
1644                 if (this.ids[i][id]) {
1645                     return this.ids[i][id];
1646                 }
1647             }
1648             return null;
1649         },
1650
1651         /**
1652          * Fired after a registered DragDrop object gets the mousedown event.
1653          * Sets up the events required to track the object being dragged
1654          * @method handleMouseDown
1655          * @param {Event} e the event
1656          * @param oDD the DragDrop object being dragged
1657          * @private
1658          * @static
1659          */
1660         handleMouseDown: function(e, oDD) {
1661             if(Roo.QuickTips){
1662                 Roo.QuickTips.disable();
1663             }
1664             this.currentTarget = e.getTarget();
1665
1666             this.dragCurrent = oDD;
1667
1668             var el = oDD.getEl();
1669
1670             // track start position
1671             this.startX = e.getPageX();
1672             this.startY = e.getPageY();
1673
1674             this.deltaX = this.startX - el.offsetLeft;
1675             this.deltaY = this.startY - el.offsetTop;
1676
1677             this.dragThreshMet = false;
1678
1679             this.clickTimeout = setTimeout(
1680                     function() {
1681                         var DDM = Roo.dd.DDM;
1682                         DDM.startDrag(DDM.startX, DDM.startY);
1683                     },
1684                     this.clickTimeThresh );
1685         },
1686
1687         /**
1688          * Fired when either the drag pixel threshol or the mousedown hold
1689          * time threshold has been met.
1690          * @method startDrag
1691          * @param x {int} the X position of the original mousedown
1692          * @param y {int} the Y position of the original mousedown
1693          * @static
1694          */
1695         startDrag: function(x, y) {
1696             clearTimeout(this.clickTimeout);
1697             if (this.dragCurrent) {
1698                 this.dragCurrent.b4StartDrag(x, y);
1699                 this.dragCurrent.startDrag(x, y);
1700             }
1701             this.dragThreshMet = true;
1702         },
1703
1704         /**
1705          * Internal function to handle the mouseup event.  Will be invoked
1706          * from the context of the document.
1707          * @method handleMouseUp
1708          * @param {Event} e the event
1709          * @private
1710          * @static
1711          */
1712         handleMouseUp: function(e) {
1713
1714             if(Roo.QuickTips){
1715                 Roo.QuickTips.enable();
1716             }
1717             if (! this.dragCurrent) {
1718                 return;
1719             }
1720
1721             clearTimeout(this.clickTimeout);
1722
1723             if (this.dragThreshMet) {
1724                 this.fireEvents(e, true);
1725             } else {
1726             }
1727
1728             this.stopDrag(e);
1729
1730             this.stopEvent(e);
1731         },
1732
1733         /**
1734          * Utility to stop event propagation and event default, if these
1735          * features are turned on.
1736          * @method stopEvent
1737          * @param {Event} e the event as returned by this.getEvent()
1738          * @static
1739          */
1740         stopEvent: function(e){
1741             if(this.stopPropagation) {
1742                 e.stopPropagation();
1743             }
1744
1745             if (this.preventDefault) {
1746                 e.preventDefault();
1747             }
1748         },
1749
1750         /**
1751          * Internal function to clean up event handlers after the drag
1752          * operation is complete
1753          * @method stopDrag
1754          * @param {Event} e the event
1755          * @private
1756          * @static
1757          */
1758         stopDrag: function(e) {
1759             // Fire the drag end event for the item that was dragged
1760             if (this.dragCurrent) {
1761                 if (this.dragThreshMet) {
1762                     this.dragCurrent.b4EndDrag(e);
1763                     this.dragCurrent.endDrag(e);
1764                 }
1765
1766                 this.dragCurrent.onMouseUp(e);
1767             }
1768
1769             this.dragCurrent = null;
1770             this.dragOvers = {};
1771         },
1772
1773         /**
1774          * Internal function to handle the mousemove event.  Will be invoked
1775          * from the context of the html element.
1776          *
1777          * @TODO figure out what we can do about mouse events lost when the
1778          * user drags objects beyond the window boundary.  Currently we can
1779          * detect this in internet explorer by verifying that the mouse is
1780          * down during the mousemove event.  Firefox doesn't give us the
1781          * button state on the mousemove event.
1782          * @method handleMouseMove
1783          * @param {Event} e the event
1784          * @private
1785          * @static
1786          */
1787         handleMouseMove: function(e) {
1788             if (! this.dragCurrent) {
1789                 return true;
1790             }
1791
1792             // var button = e.which || e.button;
1793
1794             // check for IE mouseup outside of page boundary
1795             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1796                 this.stopEvent(e);
1797                 return this.handleMouseUp(e);
1798             }
1799
1800             if (!this.dragThreshMet) {
1801                 var diffX = Math.abs(this.startX - e.getPageX());
1802                 var diffY = Math.abs(this.startY - e.getPageY());
1803                 if (diffX > this.clickPixelThresh ||
1804                             diffY > this.clickPixelThresh) {
1805                     this.startDrag(this.startX, this.startY);
1806                 }
1807             }
1808
1809             if (this.dragThreshMet) {
1810                 this.dragCurrent.b4Drag(e);
1811                 this.dragCurrent.onDrag(e);
1812                 if(!this.dragCurrent.moveOnly){
1813                     this.fireEvents(e, false);
1814                 }
1815             }
1816
1817             this.stopEvent(e);
1818
1819             return true;
1820         },
1821
1822         /**
1823          * Iterates over all of the DragDrop elements to find ones we are
1824          * hovering over or dropping on
1825          * @method fireEvents
1826          * @param {Event} e the event
1827          * @param {boolean} isDrop is this a drop op or a mouseover op?
1828          * @private
1829          * @static
1830          */
1831         fireEvents: function(e, isDrop) {
1832             var dc = this.dragCurrent;
1833
1834             // If the user did the mouse up outside of the window, we could
1835             // get here even though we have ended the drag.
1836             if (!dc || dc.isLocked()) {
1837                 return;
1838             }
1839
1840             var pt = e.getPoint();
1841
1842             // cache the previous dragOver array
1843             var oldOvers = [];
1844
1845             var outEvts   = [];
1846             var overEvts  = [];
1847             var dropEvts  = [];
1848             var enterEvts = [];
1849
1850             // Check to see if the object(s) we were hovering over is no longer
1851             // being hovered over so we can fire the onDragOut event
1852             for (var i in this.dragOvers) {
1853
1854                 var ddo = this.dragOvers[i];
1855
1856                 if (! this.isTypeOfDD(ddo)) {
1857                     continue;
1858                 }
1859
1860                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1861                     outEvts.push( ddo );
1862                 }
1863
1864                 oldOvers[i] = true;
1865                 delete this.dragOvers[i];
1866             }
1867
1868             for (var sGroup in dc.groups) {
1869
1870                 if ("string" != typeof sGroup) {
1871                     continue;
1872                 }
1873
1874                 for (i in this.ids[sGroup]) {
1875                     var oDD = this.ids[sGroup][i];
1876                     if (! this.isTypeOfDD(oDD)) {
1877                         continue;
1878                     }
1879
1880                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1881                         if (this.isOverTarget(pt, oDD, this.mode)) {
1882                             // look for drop interactions
1883                             if (isDrop) {
1884                                 dropEvts.push( oDD );
1885                             // look for drag enter and drag over interactions
1886                             } else {
1887
1888                                 // initial drag over: dragEnter fires
1889                                 if (!oldOvers[oDD.id]) {
1890                                     enterEvts.push( oDD );
1891                                 // subsequent drag overs: dragOver fires
1892                                 } else {
1893                                     overEvts.push( oDD );
1894                                 }
1895
1896                                 this.dragOvers[oDD.id] = oDD;
1897                             }
1898                         }
1899                     }
1900                 }
1901             }
1902
1903             if (this.mode) {
1904                 if (outEvts.length) {
1905                     dc.b4DragOut(e, outEvts);
1906                     dc.onDragOut(e, outEvts);
1907                 }
1908
1909                 if (enterEvts.length) {
1910                     dc.onDragEnter(e, enterEvts);
1911                 }
1912
1913                 if (overEvts.length) {
1914                     dc.b4DragOver(e, overEvts);
1915                     dc.onDragOver(e, overEvts);
1916                 }
1917
1918                 if (dropEvts.length) {
1919                     dc.b4DragDrop(e, dropEvts);
1920                     dc.onDragDrop(e, dropEvts);
1921                 }
1922
1923             } else {
1924                 // fire dragout events
1925                 var len = 0;
1926                 for (i=0, len=outEvts.length; i<len; ++i) {
1927                     dc.b4DragOut(e, outEvts[i].id);
1928                     dc.onDragOut(e, outEvts[i].id);
1929                 }
1930
1931                 // fire enter events
1932                 for (i=0,len=enterEvts.length; i<len; ++i) {
1933                     // dc.b4DragEnter(e, oDD.id);
1934                     dc.onDragEnter(e, enterEvts[i].id);
1935                 }
1936
1937                 // fire over events
1938                 for (i=0,len=overEvts.length; i<len; ++i) {
1939                     dc.b4DragOver(e, overEvts[i].id);
1940                     dc.onDragOver(e, overEvts[i].id);
1941                 }
1942
1943                 // fire drop events
1944                 for (i=0, len=dropEvts.length; i<len; ++i) {
1945                     dc.b4DragDrop(e, dropEvts[i].id);
1946                     dc.onDragDrop(e, dropEvts[i].id);
1947                 }
1948
1949             }
1950
1951             // notify about a drop that did not find a target
1952             if (isDrop && !dropEvts.length) {
1953                 dc.onInvalidDrop(e);
1954             }
1955
1956         },
1957
1958         /**
1959          * Helper function for getting the best match from the list of drag
1960          * and drop objects returned by the drag and drop events when we are
1961          * in INTERSECT mode.  It returns either the first object that the
1962          * cursor is over, or the object that has the greatest overlap with
1963          * the dragged element.
1964          * @method getBestMatch
1965          * @param  {DragDrop[]} dds The array of drag and drop objects
1966          * targeted
1967          * @return {DragDrop}       The best single match
1968          * @static
1969          */
1970         getBestMatch: function(dds) {
1971             var winner = null;
1972             // Return null if the input is not what we expect
1973             //if (!dds || !dds.length || dds.length == 0) {
1974                // winner = null;
1975             // If there is only one item, it wins
1976             //} else if (dds.length == 1) {
1977
1978             var len = dds.length;
1979
1980             if (len == 1) {
1981                 winner = dds[0];
1982             } else {
1983                 // Loop through the targeted items
1984                 for (var i=0; i<len; ++i) {
1985                     var dd = dds[i];
1986                     // If the cursor is over the object, it wins.  If the
1987                     // cursor is over multiple matches, the first one we come
1988                     // to wins.
1989                     if (dd.cursorIsOver) {
1990                         winner = dd;
1991                         break;
1992                     // Otherwise the object with the most overlap wins
1993                     } else {
1994                         if (!winner ||
1995                             winner.overlap.getArea() < dd.overlap.getArea()) {
1996                             winner = dd;
1997                         }
1998                     }
1999                 }
2000             }
2001
2002             return winner;
2003         },
2004
2005         /**
2006          * Refreshes the cache of the top-left and bottom-right points of the
2007          * drag and drop objects in the specified group(s).  This is in the
2008          * format that is stored in the drag and drop instance, so typical
2009          * usage is:
2010          * <code>
2011          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2012          * </code>
2013          * Alternatively:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2016          * </code>
2017          * @TODO this really should be an indexed array.  Alternatively this
2018          * method could accept both.
2019          * @method refreshCache
2020          * @param {Object} groups an associative array of groups to refresh
2021          * @static
2022          */
2023         refreshCache: function(groups) {
2024             for (var sGroup in groups) {
2025                 if ("string" != typeof sGroup) {
2026                     continue;
2027                 }
2028                 for (var i in this.ids[sGroup]) {
2029                     var oDD = this.ids[sGroup][i];
2030
2031                     if (this.isTypeOfDD(oDD)) {
2032                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2033                         var loc = this.getLocation(oDD);
2034                         if (loc) {
2035                             this.locationCache[oDD.id] = loc;
2036                         } else {
2037                             delete this.locationCache[oDD.id];
2038                             // this will unregister the drag and drop object if
2039                             // the element is not in a usable state
2040                             // oDD.unreg();
2041                         }
2042                     }
2043                 }
2044             }
2045         },
2046
2047         /**
2048          * This checks to make sure an element exists and is in the DOM.  The
2049          * main purpose is to handle cases where innerHTML is used to remove
2050          * drag and drop objects from the DOM.  IE provides an 'unspecified
2051          * error' when trying to access the offsetParent of such an element
2052          * @method verifyEl
2053          * @param {HTMLElement} el the element to check
2054          * @return {boolean} true if the element looks usable
2055          * @static
2056          */
2057         verifyEl: function(el) {
2058             if (el) {
2059                 var parent;
2060                 if(Roo.isIE){
2061                     try{
2062                         parent = el.offsetParent;
2063                     }catch(e){}
2064                 }else{
2065                     parent = el.offsetParent;
2066                 }
2067                 if (parent) {
2068                     return true;
2069                 }
2070             }
2071
2072             return false;
2073         },
2074
2075         /**
2076          * Returns a Region object containing the drag and drop element's position
2077          * and size, including the padding configured for it
2078          * @method getLocation
2079          * @param {DragDrop} oDD the drag and drop object to get the
2080          *                       location for
2081          * @return {Roo.lib.Region} a Region object representing the total area
2082          *                             the element occupies, including any padding
2083          *                             the instance is configured for.
2084          * @static
2085          */
2086         getLocation: function(oDD) {
2087             if (! this.isTypeOfDD(oDD)) {
2088                 return null;
2089             }
2090
2091             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2092
2093             try {
2094                 pos= Roo.lib.Dom.getXY(el);
2095             } catch (e) { }
2096
2097             if (!pos) {
2098                 return null;
2099             }
2100
2101             x1 = pos[0];
2102             x2 = x1 + el.offsetWidth;
2103             y1 = pos[1];
2104             y2 = y1 + el.offsetHeight;
2105
2106             t = y1 - oDD.padding[0];
2107             r = x2 + oDD.padding[1];
2108             b = y2 + oDD.padding[2];
2109             l = x1 - oDD.padding[3];
2110
2111             return new Roo.lib.Region( t, r, b, l );
2112         },
2113
2114         /**
2115          * Checks the cursor location to see if it over the target
2116          * @method isOverTarget
2117          * @param {Roo.lib.Point} pt The point to evaluate
2118          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2119          * @return {boolean} true if the mouse is over the target
2120          * @private
2121          * @static
2122          */
2123         isOverTarget: function(pt, oTarget, intersect) {
2124             // use cache if available
2125             var loc = this.locationCache[oTarget.id];
2126             if (!loc || !this.useCache) {
2127                 loc = this.getLocation(oTarget);
2128                 this.locationCache[oTarget.id] = loc;
2129
2130             }
2131
2132             if (!loc) {
2133                 return false;
2134             }
2135
2136             oTarget.cursorIsOver = loc.contains( pt );
2137
2138             // DragDrop is using this as a sanity check for the initial mousedown
2139             // in this case we are done.  In POINT mode, if the drag obj has no
2140             // contraints, we are also done. Otherwise we need to evaluate the
2141             // location of the target as related to the actual location of the
2142             // dragged element.
2143             var dc = this.dragCurrent;
2144             if (!dc || !dc.getTargetCoord ||
2145                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2146                 return oTarget.cursorIsOver;
2147             }
2148
2149             oTarget.overlap = null;
2150
2151             // Get the current location of the drag element, this is the
2152             // location of the mouse event less the delta that represents
2153             // where the original mousedown happened on the element.  We
2154             // need to consider constraints and ticks as well.
2155             var pos = dc.getTargetCoord(pt.x, pt.y);
2156
2157             var el = dc.getDragEl();
2158             var curRegion = new Roo.lib.Region( pos.y,
2159                                                    pos.x + el.offsetWidth,
2160                                                    pos.y + el.offsetHeight,
2161                                                    pos.x );
2162
2163             var overlap = curRegion.intersect(loc);
2164
2165             if (overlap) {
2166                 oTarget.overlap = overlap;
2167                 return (intersect) ? true : oTarget.cursorIsOver;
2168             } else {
2169                 return false;
2170             }
2171         },
2172
2173         /**
2174          * unload event handler
2175          * @method _onUnload
2176          * @private
2177          * @static
2178          */
2179         _onUnload: function(e, me) {
2180             Roo.dd.DragDropMgr.unregAll();
2181         },
2182
2183         /**
2184          * Cleans up the drag and drop events and objects.
2185          * @method unregAll
2186          * @private
2187          * @static
2188          */
2189         unregAll: function() {
2190
2191             if (this.dragCurrent) {
2192                 this.stopDrag();
2193                 this.dragCurrent = null;
2194             }
2195
2196             this._execOnAll("unreg", []);
2197
2198             for (i in this.elementCache) {
2199                 delete this.elementCache[i];
2200             }
2201
2202             this.elementCache = {};
2203             this.ids = {};
2204         },
2205
2206         /**
2207          * A cache of DOM elements
2208          * @property elementCache
2209          * @private
2210          * @static
2211          */
2212         elementCache: {},
2213
2214         /**
2215          * Get the wrapper for the DOM element specified
2216          * @method getElWrapper
2217          * @param {String} id the id of the element to get
2218          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2219          * @private
2220          * @deprecated This wrapper isn't that useful
2221          * @static
2222          */
2223         getElWrapper: function(id) {
2224             var oWrapper = this.elementCache[id];
2225             if (!oWrapper || !oWrapper.el) {
2226                 oWrapper = this.elementCache[id] =
2227                     new this.ElementWrapper(Roo.getDom(id));
2228             }
2229             return oWrapper;
2230         },
2231
2232         /**
2233          * Returns the actual DOM element
2234          * @method getElement
2235          * @param {String} id the id of the elment to get
2236          * @return {Object} The element
2237          * @deprecated use Roo.getDom instead
2238          * @static
2239          */
2240         getElement: function(id) {
2241             return Roo.getDom(id);
2242         },
2243
2244         /**
2245          * Returns the style property for the DOM element (i.e.,
2246          * document.getElById(id).style)
2247          * @method getCss
2248          * @param {String} id the id of the elment to get
2249          * @return {Object} The style property of the element
2250          * @deprecated use Roo.getDom instead
2251          * @static
2252          */
2253         getCss: function(id) {
2254             var el = Roo.getDom(id);
2255             return (el) ? el.style : null;
2256         },
2257
2258         /**
2259          * Inner class for cached elements
2260          * @class DragDropMgr.ElementWrapper
2261          * @for DragDropMgr
2262          * @private
2263          * @deprecated
2264          */
2265         ElementWrapper: function(el) {
2266                 /**
2267                  * The element
2268                  * @property el
2269                  */
2270                 this.el = el || null;
2271                 /**
2272                  * The element id
2273                  * @property id
2274                  */
2275                 this.id = this.el && el.id;
2276                 /**
2277                  * A reference to the style property
2278                  * @property css
2279                  */
2280                 this.css = this.el && el.style;
2281             },
2282
2283         /**
2284          * Returns the X position of an html element
2285          * @method getPosX
2286          * @param el the element for which to get the position
2287          * @return {int} the X coordinate
2288          * @for DragDropMgr
2289          * @deprecated use Roo.lib.Dom.getX instead
2290          * @static
2291          */
2292         getPosX: function(el) {
2293             return Roo.lib.Dom.getX(el);
2294         },
2295
2296         /**
2297          * Returns the Y position of an html element
2298          * @method getPosY
2299          * @param el the element for which to get the position
2300          * @return {int} the Y coordinate
2301          * @deprecated use Roo.lib.Dom.getY instead
2302          * @static
2303          */
2304         getPosY: function(el) {
2305             return Roo.lib.Dom.getY(el);
2306         },
2307
2308         /**
2309          * Swap two nodes.  In IE, we use the native method, for others we
2310          * emulate the IE behavior
2311          * @method swapNode
2312          * @param n1 the first node to swap
2313          * @param n2 the other node to swap
2314          * @static
2315          */
2316         swapNode: function(n1, n2) {
2317             if (n1.swapNode) {
2318                 n1.swapNode(n2);
2319             } else {
2320                 var p = n2.parentNode;
2321                 var s = n2.nextSibling;
2322
2323                 if (s == n1) {
2324                     p.insertBefore(n1, n2);
2325                 } else if (n2 == n1.nextSibling) {
2326                     p.insertBefore(n2, n1);
2327                 } else {
2328                     n1.parentNode.replaceChild(n2, n1);
2329                     p.insertBefore(n1, s);
2330                 }
2331             }
2332         },
2333
2334         /**
2335          * Returns the current scroll position
2336          * @method getScroll
2337          * @private
2338          * @static
2339          */
2340         getScroll: function () {
2341             var t, l, dde=document.documentElement, db=document.body;
2342             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2343                 t = dde.scrollTop;
2344                 l = dde.scrollLeft;
2345             } else if (db) {
2346                 t = db.scrollTop;
2347                 l = db.scrollLeft;
2348             } else {
2349
2350             }
2351             return { top: t, left: l };
2352         },
2353
2354         /**
2355          * Returns the specified element style property
2356          * @method getStyle
2357          * @param {HTMLElement} el          the element
2358          * @param {string}      styleProp   the style property
2359          * @return {string} The value of the style property
2360          * @deprecated use Roo.lib.Dom.getStyle
2361          * @static
2362          */
2363         getStyle: function(el, styleProp) {
2364             return Roo.fly(el).getStyle(styleProp);
2365         },
2366
2367         /**
2368          * Gets the scrollTop
2369          * @method getScrollTop
2370          * @return {int} the document's scrollTop
2371          * @static
2372          */
2373         getScrollTop: function () { return this.getScroll().top; },
2374
2375         /**
2376          * Gets the scrollLeft
2377          * @method getScrollLeft
2378          * @return {int} the document's scrollTop
2379          * @static
2380          */
2381         getScrollLeft: function () { return this.getScroll().left; },
2382
2383         /**
2384          * Sets the x/y position of an element to the location of the
2385          * target element.
2386          * @method moveToEl
2387          * @param {HTMLElement} moveEl      The element to move
2388          * @param {HTMLElement} targetEl    The position reference element
2389          * @static
2390          */
2391         moveToEl: function (moveEl, targetEl) {
2392             var aCoord = Roo.lib.Dom.getXY(targetEl);
2393             Roo.lib.Dom.setXY(moveEl, aCoord);
2394         },
2395
2396         /**
2397          * Numeric array sort function
2398          * @method numericSort
2399          * @static
2400          */
2401         numericSort: function(a, b) { return (a - b); },
2402
2403         /**
2404          * Internal counter
2405          * @property _timeoutCount
2406          * @private
2407          * @static
2408          */
2409         _timeoutCount: 0,
2410
2411         /**
2412          * Trying to make the load order less important.  Without this we get
2413          * an error if this file is loaded before the Event Utility.
2414          * @method _addListeners
2415          * @private
2416          * @static
2417          */
2418         _addListeners: function() {
2419             var DDM = Roo.dd.DDM;
2420             if ( Roo.lib.Event && document ) {
2421                 DDM._onLoad();
2422             } else {
2423                 if (DDM._timeoutCount > 2000) {
2424                 } else {
2425                     setTimeout(DDM._addListeners, 10);
2426                     if (document && document.body) {
2427                         DDM._timeoutCount += 1;
2428                     }
2429                 }
2430             }
2431         },
2432
2433         /**
2434          * Recursively searches the immediate parent and all child nodes for
2435          * the handle element in order to determine wheter or not it was
2436          * clicked.
2437          * @method handleWasClicked
2438          * @param node the html element to inspect
2439          * @static
2440          */
2441         handleWasClicked: function(node, id) {
2442             if (this.isHandle(id, node.id)) {
2443                 return true;
2444             } else {
2445                 // check to see if this is a text node child of the one we want
2446                 var p = node.parentNode;
2447
2448                 while (p) {
2449                     if (this.isHandle(id, p.id)) {
2450                         return true;
2451                     } else {
2452                         p = p.parentNode;
2453                     }
2454                 }
2455             }
2456
2457             return false;
2458         }
2459
2460     };
2461
2462 }();
2463
2464 // shorter alias, save a few bytes
2465 Roo.dd.DDM = Roo.dd.DragDropMgr;
2466 Roo.dd.DDM._addListeners();
2467
2468 }/*
2469  * Based on:
2470  * Ext JS Library 1.1.1
2471  * Copyright(c) 2006-2007, Ext JS, LLC.
2472  *
2473  * Originally Released Under LGPL - original licence link has changed is not relivant.
2474  *
2475  * Fork - LGPL
2476  * <script type="text/javascript">
2477  */
2478
2479 /**
2480  * @class Roo.dd.DD
2481  * A DragDrop implementation where the linked element follows the
2482  * mouse cursor during a drag.
2483  * @extends Roo.dd.DragDrop
2484  * @constructor
2485  * @param {String} id the id of the linked element
2486  * @param {String} sGroup the group of related DragDrop items
2487  * @param {object} config an object containing configurable attributes
2488  *                Valid properties for DD:
2489  *                    scroll
2490  */
2491 Roo.dd.DD = function(id, sGroup, config) {
2492     if (id) {
2493         this.init(id, sGroup, config);
2494     }
2495 };
2496
2497 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2498
2499     /**
2500      * When set to true, the utility automatically tries to scroll the browser
2501      * window wehn a drag and drop element is dragged near the viewport boundary.
2502      * Defaults to true.
2503      * @property scroll
2504      * @type boolean
2505      */
2506     scroll: true,
2507
2508     /**
2509      * Sets the pointer offset to the distance between the linked element's top
2510      * left corner and the location the element was clicked
2511      * @method autoOffset
2512      * @param {int} iPageX the X coordinate of the click
2513      * @param {int} iPageY the Y coordinate of the click
2514      */
2515     autoOffset: function(iPageX, iPageY) {
2516         var x = iPageX - this.startPageX;
2517         var y = iPageY - this.startPageY;
2518         this.setDelta(x, y);
2519     },
2520
2521     /**
2522      * Sets the pointer offset.  You can call this directly to force the
2523      * offset to be in a particular location (e.g., pass in 0,0 to set it
2524      * to the center of the object)
2525      * @method setDelta
2526      * @param {int} iDeltaX the distance from the left
2527      * @param {int} iDeltaY the distance from the top
2528      */
2529     setDelta: function(iDeltaX, iDeltaY) {
2530         this.deltaX = iDeltaX;
2531         this.deltaY = iDeltaY;
2532     },
2533
2534     /**
2535      * Sets the drag element to the location of the mousedown or click event,
2536      * maintaining the cursor location relative to the location on the element
2537      * that was clicked.  Override this if you want to place the element in a
2538      * location other than where the cursor is.
2539      * @method setDragElPos
2540      * @param {int} iPageX the X coordinate of the mousedown or drag event
2541      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2542      */
2543     setDragElPos: function(iPageX, iPageY) {
2544         // the first time we do this, we are going to check to make sure
2545         // the element has css positioning
2546
2547         var el = this.getDragEl();
2548         this.alignElWithMouse(el, iPageX, iPageY);
2549     },
2550
2551     /**
2552      * Sets the element to the location of the mousedown or click event,
2553      * maintaining the cursor location relative to the location on the element
2554      * that was clicked.  Override this if you want to place the element in a
2555      * location other than where the cursor is.
2556      * @method alignElWithMouse
2557      * @param {HTMLElement} el the element to move
2558      * @param {int} iPageX the X coordinate of the mousedown or drag event
2559      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2560      */
2561     alignElWithMouse: function(el, iPageX, iPageY) {
2562         var oCoord = this.getTargetCoord(iPageX, iPageY);
2563         var fly = el.dom ? el : Roo.fly(el);
2564         if (!this.deltaSetXY) {
2565             var aCoord = [oCoord.x, oCoord.y];
2566             fly.setXY(aCoord);
2567             var newLeft = fly.getLeft(true);
2568             var newTop  = fly.getTop(true);
2569             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2570         } else {
2571             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2572         }
2573
2574         this.cachePosition(oCoord.x, oCoord.y);
2575         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2576         return oCoord;
2577     },
2578
2579     /**
2580      * Saves the most recent position so that we can reset the constraints and
2581      * tick marks on-demand.  We need to know this so that we can calculate the
2582      * number of pixels the element is offset from its original position.
2583      * @method cachePosition
2584      * @param iPageX the current x position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      * @param iPageY the current y position (optional, this just makes it so we
2587      * don't have to look it up again)
2588      */
2589     cachePosition: function(iPageX, iPageY) {
2590         if (iPageX) {
2591             this.lastPageX = iPageX;
2592             this.lastPageY = iPageY;
2593         } else {
2594             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2595             this.lastPageX = aCoord[0];
2596             this.lastPageY = aCoord[1];
2597         }
2598     },
2599
2600     /**
2601      * Auto-scroll the window if the dragged object has been moved beyond the
2602      * visible window boundary.
2603      * @method autoScroll
2604      * @param {int} x the drag element's x position
2605      * @param {int} y the drag element's y position
2606      * @param {int} h the height of the drag element
2607      * @param {int} w the width of the drag element
2608      * @private
2609      */
2610     autoScroll: function(x, y, h, w) {
2611
2612         if (this.scroll) {
2613             // The client height
2614             var clientH = Roo.lib.Dom.getViewWidth();
2615
2616             // The client width
2617             var clientW = Roo.lib.Dom.getViewHeight();
2618
2619             // The amt scrolled down
2620             var st = this.DDM.getScrollTop();
2621
2622             // The amt scrolled right
2623             var sl = this.DDM.getScrollLeft();
2624
2625             // Location of the bottom of the element
2626             var bot = h + y;
2627
2628             // Location of the right of the element
2629             var right = w + x;
2630
2631             // The distance from the cursor to the bottom of the visible area,
2632             // adjusted so that we don't scroll if the cursor is beyond the
2633             // element drag constraints
2634             var toBot = (clientH + st - y - this.deltaY);
2635
2636             // The distance from the cursor to the right of the visible area
2637             var toRight = (clientW + sl - x - this.deltaX);
2638
2639
2640             // How close to the edge the cursor must be before we scroll
2641             // var thresh = (document.all) ? 100 : 40;
2642             var thresh = 40;
2643
2644             // How many pixels to scroll per autoscroll op.  This helps to reduce
2645             // clunky scrolling. IE is more sensitive about this ... it needs this
2646             // value to be higher.
2647             var scrAmt = (document.all) ? 80 : 30;
2648
2649             // Scroll down if we are near the bottom of the visible page and the
2650             // obj extends below the crease
2651             if ( bot > clientH && toBot < thresh ) {
2652                 window.scrollTo(sl, st + scrAmt);
2653             }
2654
2655             // Scroll up if the window is scrolled down and the top of the object
2656             // goes above the top border
2657             if ( y < st && st > 0 && y - st < thresh ) {
2658                 window.scrollTo(sl, st - scrAmt);
2659             }
2660
2661             // Scroll right if the obj is beyond the right border and the cursor is
2662             // near the border.
2663             if ( right > clientW && toRight < thresh ) {
2664                 window.scrollTo(sl + scrAmt, st);
2665             }
2666
2667             // Scroll left if the window has been scrolled to the right and the obj
2668             // extends past the left border
2669             if ( x < sl && sl > 0 && x - sl < thresh ) {
2670                 window.scrollTo(sl - scrAmt, st);
2671             }
2672         }
2673     },
2674
2675     /**
2676      * Finds the location the element should be placed if we want to move
2677      * it to where the mouse location less the click offset would place us.
2678      * @method getTargetCoord
2679      * @param {int} iPageX the X coordinate of the click
2680      * @param {int} iPageY the Y coordinate of the click
2681      * @return an object that contains the coordinates (Object.x and Object.y)
2682      * @private
2683      */
2684     getTargetCoord: function(iPageX, iPageY) {
2685
2686
2687         var x = iPageX - this.deltaX;
2688         var y = iPageY - this.deltaY;
2689
2690         if (this.constrainX) {
2691             if (x < this.minX) { x = this.minX; }
2692             if (x > this.maxX) { x = this.maxX; }
2693         }
2694
2695         if (this.constrainY) {
2696             if (y < this.minY) { y = this.minY; }
2697             if (y > this.maxY) { y = this.maxY; }
2698         }
2699
2700         x = this.getTick(x, this.xTicks);
2701         y = this.getTick(y, this.yTicks);
2702
2703
2704         return {x:x, y:y};
2705     },
2706
2707     /*
2708      * Sets up config options specific to this class. Overrides
2709      * Roo.dd.DragDrop, but all versions of this method through the
2710      * inheritance chain are called
2711      */
2712     applyConfig: function() {
2713         Roo.dd.DD.superclass.applyConfig.call(this);
2714         this.scroll = (this.config.scroll !== false);
2715     },
2716
2717     /*
2718      * Event that fires prior to the onMouseDown event.  Overrides
2719      * Roo.dd.DragDrop.
2720      */
2721     b4MouseDown: function(e) {
2722         // this.resetConstraints();
2723         this.autoOffset(e.getPageX(),
2724                             e.getPageY());
2725     },
2726
2727     /*
2728      * Event that fires prior to the onDrag event.  Overrides
2729      * Roo.dd.DragDrop.
2730      */
2731     b4Drag: function(e) {
2732         this.setDragElPos(e.getPageX(),
2733                             e.getPageY());
2734     },
2735
2736     toString: function() {
2737         return ("DD " + this.id);
2738     }
2739
2740     //////////////////////////////////////////////////////////////////////////
2741     // Debugging ygDragDrop events that can be overridden
2742     //////////////////////////////////////////////////////////////////////////
2743     /*
2744     startDrag: function(x, y) {
2745     },
2746
2747     onDrag: function(e) {
2748     },
2749
2750     onDragEnter: function(e, id) {
2751     },
2752
2753     onDragOver: function(e, id) {
2754     },
2755
2756     onDragOut: function(e, id) {
2757     },
2758
2759     onDragDrop: function(e, id) {
2760     },
2761
2762     endDrag: function(e) {
2763     }
2764
2765     */
2766
2767 });/*
2768  * Based on:
2769  * Ext JS Library 1.1.1
2770  * Copyright(c) 2006-2007, Ext JS, LLC.
2771  *
2772  * Originally Released Under LGPL - original licence link has changed is not relivant.
2773  *
2774  * Fork - LGPL
2775  * <script type="text/javascript">
2776  */
2777
2778 /**
2779  * @class Roo.dd.DDProxy
2780  * A DragDrop implementation that inserts an empty, bordered div into
2781  * the document that follows the cursor during drag operations.  At the time of
2782  * the click, the frame div is resized to the dimensions of the linked html
2783  * element, and moved to the exact location of the linked element.
2784  *
2785  * References to the "frame" element refer to the single proxy element that
2786  * was created to be dragged in place of all DDProxy elements on the
2787  * page.
2788  *
2789  * @extends Roo.dd.DD
2790  * @constructor
2791  * @param {String} id the id of the linked html element
2792  * @param {String} sGroup the group of related DragDrop objects
2793  * @param {object} config an object containing configurable attributes
2794  *                Valid properties for DDProxy in addition to those in DragDrop:
2795  *                   resizeFrame, centerFrame, dragElId
2796  */
2797 Roo.dd.DDProxy = function(id, sGroup, config) {
2798     if (id) {
2799         this.init(id, sGroup, config);
2800         this.initFrame();
2801     }
2802 };
2803
2804 /**
2805  * The default drag frame div id
2806  * @property Roo.dd.DDProxy.dragElId
2807  * @type String
2808  * @static
2809  */
2810 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2811
2812 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2813
2814     /**
2815      * By default we resize the drag frame to be the same size as the element
2816      * we want to drag (this is to get the frame effect).  We can turn it off
2817      * if we want a different behavior.
2818      * @property resizeFrame
2819      * @type boolean
2820      */
2821     resizeFrame: true,
2822
2823     /**
2824      * By default the frame is positioned exactly where the drag element is, so
2825      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2826      * you do not have constraints on the obj is to have the drag frame centered
2827      * around the cursor.  Set centerFrame to true for this effect.
2828      * @property centerFrame
2829      * @type boolean
2830      */
2831     centerFrame: false,
2832
2833     /**
2834      * Creates the proxy element if it does not yet exist
2835      * @method createFrame
2836      */
2837     createFrame: function() {
2838         var self = this;
2839         var body = document.body;
2840
2841         if (!body || !body.firstChild) {
2842             setTimeout( function() { self.createFrame(); }, 50 );
2843             return;
2844         }
2845
2846         var div = this.getDragEl();
2847
2848         if (!div) {
2849             div    = document.createElement("div");
2850             div.id = this.dragElId;
2851             var s  = div.style;
2852
2853             s.position   = "absolute";
2854             s.visibility = "hidden";
2855             s.cursor     = "move";
2856             s.border     = "2px solid #aaa";
2857             s.zIndex     = 999;
2858
2859             // appendChild can blow up IE if invoked prior to the window load event
2860             // while rendering a table.  It is possible there are other scenarios
2861             // that would cause this to happen as well.
2862             body.insertBefore(div, body.firstChild);
2863         }
2864     },
2865
2866     /**
2867      * Initialization for the drag frame element.  Must be called in the
2868      * constructor of all subclasses
2869      * @method initFrame
2870      */
2871     initFrame: function() {
2872         this.createFrame();
2873     },
2874
2875     applyConfig: function() {
2876         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2877
2878         this.resizeFrame = (this.config.resizeFrame !== false);
2879         this.centerFrame = (this.config.centerFrame);
2880         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2881     },
2882
2883     /**
2884      * Resizes the drag frame to the dimensions of the clicked object, positions
2885      * it over the object, and finally displays it
2886      * @method showFrame
2887      * @param {int} iPageX X click position
2888      * @param {int} iPageY Y click position
2889      * @private
2890      */
2891     showFrame: function(iPageX, iPageY) {
2892         var el = this.getEl();
2893         var dragEl = this.getDragEl();
2894         var s = dragEl.style;
2895
2896         this._resizeProxy();
2897
2898         if (this.centerFrame) {
2899             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2900                            Math.round(parseInt(s.height, 10)/2) );
2901         }
2902
2903         this.setDragElPos(iPageX, iPageY);
2904
2905         Roo.fly(dragEl).show();
2906     },
2907
2908     /**
2909      * The proxy is automatically resized to the dimensions of the linked
2910      * element when a drag is initiated, unless resizeFrame is set to false
2911      * @method _resizeProxy
2912      * @private
2913      */
2914     _resizeProxy: function() {
2915         if (this.resizeFrame) {
2916             var el = this.getEl();
2917             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2918         }
2919     },
2920
2921     // overrides Roo.dd.DragDrop
2922     b4MouseDown: function(e) {
2923         var x = e.getPageX();
2924         var y = e.getPageY();
2925         this.autoOffset(x, y);
2926         this.setDragElPos(x, y);
2927     },
2928
2929     // overrides Roo.dd.DragDrop
2930     b4StartDrag: function(x, y) {
2931         // show the drag frame
2932         this.showFrame(x, y);
2933     },
2934
2935     // overrides Roo.dd.DragDrop
2936     b4EndDrag: function(e) {
2937         Roo.fly(this.getDragEl()).hide();
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     // By default we try to move the element to the last location of the frame.
2942     // This is so that the default behavior mirrors that of Roo.dd.DD.
2943     endDrag: function(e) {
2944
2945         var lel = this.getEl();
2946         var del = this.getDragEl();
2947
2948         // Show the drag frame briefly so we can get its position
2949         del.style.visibility = "";
2950
2951         this.beforeMove();
2952         // Hide the linked element before the move to get around a Safari
2953         // rendering bug.
2954         lel.style.visibility = "hidden";
2955         Roo.dd.DDM.moveToEl(lel, del);
2956         del.style.visibility = "hidden";
2957         lel.style.visibility = "";
2958
2959         this.afterDrag();
2960     },
2961
2962     beforeMove : function(){
2963
2964     },
2965
2966     afterDrag : function(){
2967
2968     },
2969
2970     toString: function() {
2971         return ("DDProxy " + this.id);
2972     }
2973
2974 });
2975 /*
2976  * Based on:
2977  * Ext JS Library 1.1.1
2978  * Copyright(c) 2006-2007, Ext JS, LLC.
2979  *
2980  * Originally Released Under LGPL - original licence link has changed is not relivant.
2981  *
2982  * Fork - LGPL
2983  * <script type="text/javascript">
2984  */
2985
2986  /**
2987  * @class Roo.dd.DDTarget
2988  * A DragDrop implementation that does not move, but can be a drop
2989  * target.  You would get the same result by simply omitting implementation
2990  * for the event callbacks, but this way we reduce the processing cost of the
2991  * event listener and the callbacks.
2992  * @extends Roo.dd.DragDrop
2993  * @constructor
2994  * @param {String} id the id of the element that is a drop target
2995  * @param {String} sGroup the group of related DragDrop objects
2996  * @param {object} config an object containing configurable attributes
2997  *                 Valid properties for DDTarget in addition to those in
2998  *                 DragDrop:
2999  *                    none
3000  */
3001 Roo.dd.DDTarget = function(id, sGroup, config) {
3002     if (id) {
3003         this.initTarget(id, sGroup, config);
3004     }
3005     if (config.listeners || config.events) { 
3006        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3007             listeners : config.listeners || {}, 
3008             events : config.events || {} 
3009         });    
3010     }
3011 };
3012
3013 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3014 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3015     toString: function() {
3016         return ("DDTarget " + this.id);
3017     }
3018 });
3019 /*
3020  * Based on:
3021  * Ext JS Library 1.1.1
3022  * Copyright(c) 2006-2007, Ext JS, LLC.
3023  *
3024  * Originally Released Under LGPL - original licence link has changed is not relivant.
3025  *
3026  * Fork - LGPL
3027  * <script type="text/javascript">
3028  */
3029  
3030
3031 /**
3032  * @class Roo.dd.ScrollManager
3033  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3034  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3035  * @singleton
3036  */
3037 Roo.dd.ScrollManager = function(){
3038     var ddm = Roo.dd.DragDropMgr;
3039     var els = {};
3040     var dragEl = null;
3041     var proc = {};
3042     
3043     
3044     
3045     var onStop = function(e){
3046         dragEl = null;
3047         clearProc();
3048     };
3049     
3050     var triggerRefresh = function(){
3051         if(ddm.dragCurrent){
3052              ddm.refreshCache(ddm.dragCurrent.groups);
3053         }
3054     };
3055     
3056     var doScroll = function(){
3057         if(ddm.dragCurrent){
3058             var dds = Roo.dd.ScrollManager;
3059             if(!dds.animate){
3060                 if(proc.el.scroll(proc.dir, dds.increment)){
3061                     triggerRefresh();
3062                 }
3063             }else{
3064                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3065             }
3066         }
3067     };
3068     
3069     var clearProc = function(){
3070         if(proc.id){
3071             clearInterval(proc.id);
3072         }
3073         proc.id = 0;
3074         proc.el = null;
3075         proc.dir = "";
3076     };
3077     
3078     var startProc = function(el, dir){
3079          Roo.log('scroll startproc');
3080         clearProc();
3081         proc.el = el;
3082         proc.dir = dir;
3083         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3084     };
3085     
3086     var onFire = function(e, isDrop){
3087        
3088         if(isDrop || !ddm.dragCurrent){ return; }
3089         var dds = Roo.dd.ScrollManager;
3090         if(!dragEl || dragEl != ddm.dragCurrent){
3091             dragEl = ddm.dragCurrent;
3092             // refresh regions on drag start
3093             dds.refreshCache();
3094         }
3095         
3096         var xy = Roo.lib.Event.getXY(e);
3097         var pt = new Roo.lib.Point(xy[0], xy[1]);
3098         for(var id in els){
3099             var el = els[id], r = el._region;
3100             if(r && r.contains(pt) && el.isScrollable()){
3101                 if(r.bottom - pt.y <= dds.thresh){
3102                     if(proc.el != el){
3103                         startProc(el, "down");
3104                     }
3105                     return;
3106                 }else if(r.right - pt.x <= dds.thresh){
3107                     if(proc.el != el){
3108                         startProc(el, "left");
3109                     }
3110                     return;
3111                 }else if(pt.y - r.top <= dds.thresh){
3112                     if(proc.el != el){
3113                         startProc(el, "up");
3114                     }
3115                     return;
3116                 }else if(pt.x - r.left <= dds.thresh){
3117                     if(proc.el != el){
3118                         startProc(el, "right");
3119                     }
3120                     return;
3121                 }
3122             }
3123         }
3124         clearProc();
3125     };
3126     
3127     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3128     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3129     
3130     return {
3131         /**
3132          * Registers new overflow element(s) to auto scroll
3133          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3134          */
3135         register : function(el){
3136             if(el instanceof Array){
3137                 for(var i = 0, len = el.length; i < len; i++) {
3138                         this.register(el[i]);
3139                 }
3140             }else{
3141                 el = Roo.get(el);
3142                 els[el.id] = el;
3143             }
3144             Roo.dd.ScrollManager.els = els;
3145         },
3146         
3147         /**
3148          * Unregisters overflow element(s) so they are no longer scrolled
3149          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3150          */
3151         unregister : function(el){
3152             if(el instanceof Array){
3153                 for(var i = 0, len = el.length; i < len; i++) {
3154                         this.unregister(el[i]);
3155                 }
3156             }else{
3157                 el = Roo.get(el);
3158                 delete els[el.id];
3159             }
3160         },
3161         
3162         /**
3163          * The number of pixels from the edge of a container the pointer needs to be to 
3164          * trigger scrolling (defaults to 25)
3165          * @type Number
3166          */
3167         thresh : 25,
3168         
3169         /**
3170          * The number of pixels to scroll in each scroll increment (defaults to 50)
3171          * @type Number
3172          */
3173         increment : 100,
3174         
3175         /**
3176          * The frequency of scrolls in milliseconds (defaults to 500)
3177          * @type Number
3178          */
3179         frequency : 500,
3180         
3181         /**
3182          * True to animate the scroll (defaults to true)
3183          * @type Boolean
3184          */
3185         animate: true,
3186         
3187         /**
3188          * The animation duration in seconds - 
3189          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3190          * @type Number
3191          */
3192         animDuration: .4,
3193         
3194         /**
3195          * Manually trigger a cache refresh.
3196          */
3197         refreshCache : function(){
3198             for(var id in els){
3199                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3200                     els[id]._region = els[id].getRegion();
3201                 }
3202             }
3203         }
3204     };
3205 }();/*
3206  * Based on:
3207  * Ext JS Library 1.1.1
3208  * Copyright(c) 2006-2007, Ext JS, LLC.
3209  *
3210  * Originally Released Under LGPL - original licence link has changed is not relivant.
3211  *
3212  * Fork - LGPL
3213  * <script type="text/javascript">
3214  */
3215  
3216
3217 /**
3218  * @class Roo.dd.Registry
3219  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3220  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3221  * @singleton
3222  */
3223 Roo.dd.Registry = function(){
3224     var elements = {}; 
3225     var handles = {}; 
3226     var autoIdSeed = 0;
3227
3228     var getId = function(el, autogen){
3229         if(typeof el == "string"){
3230             return el;
3231         }
3232         var id = el.id;
3233         if(!id && autogen !== false){
3234             id = "roodd-" + (++autoIdSeed);
3235             el.id = id;
3236         }
3237         return id;
3238     };
3239     
3240     return {
3241     /**
3242      * Register a drag drop element
3243      * @param {String|HTMLElement} element The id or DOM node to register
3244      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3245      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3246      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3247      * populated in the data object (if applicable):
3248      * <pre>
3249 Value      Description<br />
3250 ---------  ------------------------------------------<br />
3251 handles    Array of DOM nodes that trigger dragging<br />
3252            for the element being registered<br />
3253 isHandle   True if the element passed in triggers<br />
3254            dragging itself, else false
3255 </pre>
3256      */
3257         register : function(el, data){
3258             data = data || {};
3259             if(typeof el == "string"){
3260                 el = document.getElementById(el);
3261             }
3262             data.ddel = el;
3263             elements[getId(el)] = data;
3264             if(data.isHandle !== false){
3265                 handles[data.ddel.id] = data;
3266             }
3267             if(data.handles){
3268                 var hs = data.handles;
3269                 for(var i = 0, len = hs.length; i < len; i++){
3270                         handles[getId(hs[i])] = data;
3271                 }
3272             }
3273         },
3274
3275     /**
3276      * Unregister a drag drop element
3277      * @param {String|HTMLElement}  element The id or DOM node to unregister
3278      */
3279         unregister : function(el){
3280             var id = getId(el, false);
3281             var data = elements[id];
3282             if(data){
3283                 delete elements[id];
3284                 if(data.handles){
3285                     var hs = data.handles;
3286                     for(var i = 0, len = hs.length; i < len; i++){
3287                         delete handles[getId(hs[i], false)];
3288                     }
3289                 }
3290             }
3291         },
3292
3293     /**
3294      * Returns the handle registered for a DOM Node by id
3295      * @param {String|HTMLElement} id The DOM node or id to look up
3296      * @return {Object} handle The custom handle data
3297      */
3298         getHandle : function(id){
3299             if(typeof id != "string"){ // must be element?
3300                 id = id.id;
3301             }
3302             return handles[id];
3303         },
3304
3305     /**
3306      * Returns the handle that is registered for the DOM node that is the target of the event
3307      * @param {Event} e The event
3308      * @return {Object} handle The custom handle data
3309      */
3310         getHandleFromEvent : function(e){
3311             var t = Roo.lib.Event.getTarget(e);
3312             return t ? handles[t.id] : null;
3313         },
3314
3315     /**
3316      * Returns a custom data object that is registered for a DOM node by id
3317      * @param {String|HTMLElement} id The DOM node or id to look up
3318      * @return {Object} data The custom data
3319      */
3320         getTarget : function(id){
3321             if(typeof id != "string"){ // must be element?
3322                 id = id.id;
3323             }
3324             return elements[id];
3325         },
3326
3327     /**
3328      * Returns a custom data object that is registered for the DOM node that is the target of the event
3329      * @param {Event} e The event
3330      * @return {Object} data The custom data
3331      */
3332         getTargetFromEvent : function(e){
3333             var t = Roo.lib.Event.getTarget(e);
3334             return t ? elements[t.id] || handles[t.id] : null;
3335         }
3336     };
3337 }();/*
3338  * Based on:
3339  * Ext JS Library 1.1.1
3340  * Copyright(c) 2006-2007, Ext JS, LLC.
3341  *
3342  * Originally Released Under LGPL - original licence link has changed is not relivant.
3343  *
3344  * Fork - LGPL
3345  * <script type="text/javascript">
3346  */
3347  
3348
3349 /**
3350  * @class Roo.dd.StatusProxy
3351  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3352  * default drag proxy used by all Roo.dd components.
3353  * @constructor
3354  * @param {Object} config
3355  */
3356 Roo.dd.StatusProxy = function(config){
3357     Roo.apply(this, config);
3358     this.id = this.id || Roo.id();
3359     this.el = new Roo.Layer({
3360         dh: {
3361             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3362                 {tag: "div", cls: "x-dd-drop-icon"},
3363                 {tag: "div", cls: "x-dd-drag-ghost"}
3364             ]
3365         }, 
3366         shadow: !config || config.shadow !== false
3367     });
3368     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3369     this.dropStatus = this.dropNotAllowed;
3370 };
3371
3372 Roo.dd.StatusProxy.prototype = {
3373     /**
3374      * @cfg {String} dropAllowed
3375      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3376      */
3377     dropAllowed : "x-dd-drop-ok",
3378     /**
3379      * @cfg {String} dropNotAllowed
3380      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3381      */
3382     dropNotAllowed : "x-dd-drop-nodrop",
3383
3384     /**
3385      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3386      * over the current target element.
3387      * @param {String} cssClass The css class for the new drop status indicator image
3388      */
3389     setStatus : function(cssClass){
3390         cssClass = cssClass || this.dropNotAllowed;
3391         if(this.dropStatus != cssClass){
3392             this.el.replaceClass(this.dropStatus, cssClass);
3393             this.dropStatus = cssClass;
3394         }
3395     },
3396
3397     /**
3398      * Resets the status indicator to the default dropNotAllowed value
3399      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3400      */
3401     reset : function(clearGhost){
3402         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3403         this.dropStatus = this.dropNotAllowed;
3404         if(clearGhost){
3405             this.ghost.update("");
3406         }
3407     },
3408
3409     /**
3410      * Updates the contents of the ghost element
3411      * @param {String} html The html that will replace the current innerHTML of the ghost element
3412      */
3413     update : function(html){
3414         if(typeof html == "string"){
3415             this.ghost.update(html);
3416         }else{
3417             this.ghost.update("");
3418             html.style.margin = "0";
3419             this.ghost.dom.appendChild(html);
3420         }
3421         // ensure float = none set?? cant remember why though.
3422         var el = this.ghost.dom.firstChild;
3423                 if(el){
3424                         Roo.fly(el).setStyle('float', 'none');
3425                 }
3426     },
3427     
3428     /**
3429      * Returns the underlying proxy {@link Roo.Layer}
3430      * @return {Roo.Layer} el
3431     */
3432     getEl : function(){
3433         return this.el;
3434     },
3435
3436     /**
3437      * Returns the ghost element
3438      * @return {Roo.Element} el
3439      */
3440     getGhost : function(){
3441         return this.ghost;
3442     },
3443
3444     /**
3445      * Hides the proxy
3446      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3447      */
3448     hide : function(clear){
3449         this.el.hide();
3450         if(clear){
3451             this.reset(true);
3452         }
3453     },
3454
3455     /**
3456      * Stops the repair animation if it's currently running
3457      */
3458     stop : function(){
3459         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3460             this.anim.stop();
3461         }
3462     },
3463
3464     /**
3465      * Displays this proxy
3466      */
3467     show : function(){
3468         this.el.show();
3469     },
3470
3471     /**
3472      * Force the Layer to sync its shadow and shim positions to the element
3473      */
3474     sync : function(){
3475         this.el.sync();
3476     },
3477
3478     /**
3479      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3480      * invalid drop operation by the item being dragged.
3481      * @param {Array} xy The XY position of the element ([x, y])
3482      * @param {Function} callback The function to call after the repair is complete
3483      * @param {Object} scope The scope in which to execute the callback
3484      */
3485     repair : function(xy, callback, scope){
3486         this.callback = callback;
3487         this.scope = scope;
3488         if(xy && this.animRepair !== false){
3489             this.el.addClass("x-dd-drag-repair");
3490             this.el.hideUnders(true);
3491             this.anim = this.el.shift({
3492                 duration: this.repairDuration || .5,
3493                 easing: 'easeOut',
3494                 xy: xy,
3495                 stopFx: true,
3496                 callback: this.afterRepair,
3497                 scope: this
3498             });
3499         }else{
3500             this.afterRepair();
3501         }
3502     },
3503
3504     // private
3505     afterRepair : function(){
3506         this.hide(true);
3507         if(typeof this.callback == "function"){
3508             this.callback.call(this.scope || this);
3509         }
3510         this.callback = null;
3511         this.scope = null;
3512     }
3513 };/*
3514  * Based on:
3515  * Ext JS Library 1.1.1
3516  * Copyright(c) 2006-2007, Ext JS, LLC.
3517  *
3518  * Originally Released Under LGPL - original licence link has changed is not relivant.
3519  *
3520  * Fork - LGPL
3521  * <script type="text/javascript">
3522  */
3523
3524 /**
3525  * @class Roo.dd.DragSource
3526  * @extends Roo.dd.DDProxy
3527  * A simple class that provides the basic implementation needed to make any element draggable.
3528  * @constructor
3529  * @param {String/HTMLElement/Element} el The container element
3530  * @param {Object} config
3531  */
3532 Roo.dd.DragSource = function(el, config){
3533     this.el = Roo.get(el);
3534     this.dragData = {};
3535     
3536     Roo.apply(this, config);
3537     
3538     if(!this.proxy){
3539         this.proxy = new Roo.dd.StatusProxy();
3540     }
3541
3542     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3543           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3544     
3545     this.dragging = false;
3546 };
3547
3548 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3549     /**
3550      * @cfg {String} dropAllowed
3551      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3552      */
3553     dropAllowed : "x-dd-drop-ok",
3554     /**
3555      * @cfg {String} dropNotAllowed
3556      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3557      */
3558     dropNotAllowed : "x-dd-drop-nodrop",
3559
3560     /**
3561      * Returns the data object associated with this drag source
3562      * @return {Object} data An object containing arbitrary data
3563      */
3564     getDragData : function(e){
3565         return this.dragData;
3566     },
3567
3568     // private
3569     onDragEnter : function(e, id){
3570         var target = Roo.dd.DragDropMgr.getDDById(id);
3571         this.cachedTarget = target;
3572         if(this.beforeDragEnter(target, e, id) !== false){
3573             if(target.isNotifyTarget){
3574                 var status = target.notifyEnter(this, e, this.dragData);
3575                 this.proxy.setStatus(status);
3576             }else{
3577                 this.proxy.setStatus(this.dropAllowed);
3578             }
3579             
3580             if(this.afterDragEnter){
3581                 /**
3582                  * An empty function by default, but provided so that you can perform a custom action
3583                  * when the dragged item enters the drop target by providing an implementation.
3584                  * @param {Roo.dd.DragDrop} target The drop target
3585                  * @param {Event} e The event object
3586                  * @param {String} id The id of the dragged element
3587                  * @method afterDragEnter
3588                  */
3589                 this.afterDragEnter(target, e, id);
3590             }
3591         }
3592     },
3593
3594     /**
3595      * An empty function by default, but provided so that you can perform a custom action
3596      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3597      * @param {Roo.dd.DragDrop} target The drop target
3598      * @param {Event} e The event object
3599      * @param {String} id The id of the dragged element
3600      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3601      */
3602     beforeDragEnter : function(target, e, id){
3603         return true;
3604     },
3605
3606     // private
3607     alignElWithMouse: function() {
3608         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3609         this.proxy.sync();
3610     },
3611
3612     // private
3613     onDragOver : function(e, id){
3614         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3615         if(this.beforeDragOver(target, e, id) !== false){
3616             if(target.isNotifyTarget){
3617                 var status = target.notifyOver(this, e, this.dragData);
3618                 this.proxy.setStatus(status);
3619             }
3620
3621             if(this.afterDragOver){
3622                 /**
3623                  * An empty function by default, but provided so that you can perform a custom action
3624                  * while the dragged item is over the drop target by providing an implementation.
3625                  * @param {Roo.dd.DragDrop} target The drop target
3626                  * @param {Event} e The event object
3627                  * @param {String} id The id of the dragged element
3628                  * @method afterDragOver
3629                  */
3630                 this.afterDragOver(target, e, id);
3631             }
3632         }
3633     },
3634
3635     /**
3636      * An empty function by default, but provided so that you can perform a custom action
3637      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3638      * @param {Roo.dd.DragDrop} target The drop target
3639      * @param {Event} e The event object
3640      * @param {String} id The id of the dragged element
3641      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3642      */
3643     beforeDragOver : function(target, e, id){
3644         return true;
3645     },
3646
3647     // private
3648     onDragOut : function(e, id){
3649         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3650         if(this.beforeDragOut(target, e, id) !== false){
3651             if(target.isNotifyTarget){
3652                 target.notifyOut(this, e, this.dragData);
3653             }
3654             this.proxy.reset();
3655             if(this.afterDragOut){
3656                 /**
3657                  * An empty function by default, but provided so that you can perform a custom action
3658                  * after the dragged item is dragged out of the target without dropping.
3659                  * @param {Roo.dd.DragDrop} target The drop target
3660                  * @param {Event} e The event object
3661                  * @param {String} id The id of the dragged element
3662                  * @method afterDragOut
3663                  */
3664                 this.afterDragOut(target, e, id);
3665             }
3666         }
3667         this.cachedTarget = null;
3668     },
3669
3670     /**
3671      * An empty function by default, but provided so that you can perform a custom action before the dragged
3672      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3673      * @param {Roo.dd.DragDrop} target The drop target
3674      * @param {Event} e The event object
3675      * @param {String} id The id of the dragged element
3676      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3677      */
3678     beforeDragOut : function(target, e, id){
3679         return true;
3680     },
3681     
3682     // private
3683     onDragDrop : function(e, id){
3684         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3685         if(this.beforeDragDrop(target, e, id) !== false){
3686             if(target.isNotifyTarget){
3687                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3688                     this.onValidDrop(target, e, id);
3689                 }else{
3690                     this.onInvalidDrop(target, e, id);
3691                 }
3692             }else{
3693                 this.onValidDrop(target, e, id);
3694             }
3695             
3696             if(this.afterDragDrop){
3697                 /**
3698                  * An empty function by default, but provided so that you can perform a custom action
3699                  * after a valid drag drop has occurred by providing an implementation.
3700                  * @param {Roo.dd.DragDrop} target The drop target
3701                  * @param {Event} e The event object
3702                  * @param {String} id The id of the dropped element
3703                  * @method afterDragDrop
3704                  */
3705                 this.afterDragDrop(target, e, id);
3706             }
3707         }
3708         delete this.cachedTarget;
3709     },
3710
3711     /**
3712      * An empty function by default, but provided so that you can perform a custom action before the dragged
3713      * item is dropped onto the target and optionally cancel the onDragDrop.
3714      * @param {Roo.dd.DragDrop} target The drop target
3715      * @param {Event} e The event object
3716      * @param {String} id The id of the dragged element
3717      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3718      */
3719     beforeDragDrop : function(target, e, id){
3720         return true;
3721     },
3722
3723     // private
3724     onValidDrop : function(target, e, id){
3725         this.hideProxy();
3726         if(this.afterValidDrop){
3727             /**
3728              * An empty function by default, but provided so that you can perform a custom action
3729              * after a valid drop has occurred by providing an implementation.
3730              * @param {Object} target The target DD 
3731              * @param {Event} e The event object
3732              * @param {String} id The id of the dropped element
3733              * @method afterInvalidDrop
3734              */
3735             this.afterValidDrop(target, e, id);
3736         }
3737     },
3738
3739     // private
3740     getRepairXY : function(e, data){
3741         return this.el.getXY();  
3742     },
3743
3744     // private
3745     onInvalidDrop : function(target, e, id){
3746         this.beforeInvalidDrop(target, e, id);
3747         if(this.cachedTarget){
3748             if(this.cachedTarget.isNotifyTarget){
3749                 this.cachedTarget.notifyOut(this, e, this.dragData);
3750             }
3751             this.cacheTarget = null;
3752         }
3753         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3754
3755         if(this.afterInvalidDrop){
3756             /**
3757              * An empty function by default, but provided so that you can perform a custom action
3758              * after an invalid drop has occurred by providing an implementation.
3759              * @param {Event} e The event object
3760              * @param {String} id The id of the dropped element
3761              * @method afterInvalidDrop
3762              */
3763             this.afterInvalidDrop(e, id);
3764         }
3765     },
3766
3767     // private
3768     afterRepair : function(){
3769         if(Roo.enableFx){
3770             this.el.highlight(this.hlColor || "c3daf9");
3771         }
3772         this.dragging = false;
3773     },
3774
3775     /**
3776      * An empty function by default, but provided so that you can perform a custom action after an invalid
3777      * drop has occurred.
3778      * @param {Roo.dd.DragDrop} target The drop target
3779      * @param {Event} e The event object
3780      * @param {String} id The id of the dragged element
3781      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3782      */
3783     beforeInvalidDrop : function(target, e, id){
3784         return true;
3785     },
3786
3787     // private
3788     handleMouseDown : function(e){
3789         if(this.dragging) {
3790             return;
3791         }
3792         var data = this.getDragData(e);
3793         if(data && this.onBeforeDrag(data, e) !== false){
3794             this.dragData = data;
3795             this.proxy.stop();
3796             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3797         } 
3798     },
3799
3800     /**
3801      * An empty function by default, but provided so that you can perform a custom action before the initial
3802      * drag event begins and optionally cancel it.
3803      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3804      * @param {Event} e The event object
3805      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3806      */
3807     onBeforeDrag : function(data, e){
3808         return true;
3809     },
3810
3811     /**
3812      * An empty function by default, but provided so that you can perform a custom action once the initial
3813      * drag event has begun.  The drag cannot be canceled from this function.
3814      * @param {Number} x The x position of the click on the dragged object
3815      * @param {Number} y The y position of the click on the dragged object
3816      */
3817     onStartDrag : Roo.emptyFn,
3818
3819     // private - YUI override
3820     startDrag : function(x, y){
3821         this.proxy.reset();
3822         this.dragging = true;
3823         this.proxy.update("");
3824         this.onInitDrag(x, y);
3825         this.proxy.show();
3826     },
3827
3828     // private
3829     onInitDrag : function(x, y){
3830         var clone = this.el.dom.cloneNode(true);
3831         clone.id = Roo.id(); // prevent duplicate ids
3832         this.proxy.update(clone);
3833         this.onStartDrag(x, y);
3834         return true;
3835     },
3836
3837     /**
3838      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3839      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3840      */
3841     getProxy : function(){
3842         return this.proxy;  
3843     },
3844
3845     /**
3846      * Hides the drag source's {@link Roo.dd.StatusProxy}
3847      */
3848     hideProxy : function(){
3849         this.proxy.hide();  
3850         this.proxy.reset(true);
3851         this.dragging = false;
3852     },
3853
3854     // private
3855     triggerCacheRefresh : function(){
3856         Roo.dd.DDM.refreshCache(this.groups);
3857     },
3858
3859     // private - override to prevent hiding
3860     b4EndDrag: function(e) {
3861     },
3862
3863     // private - override to prevent moving
3864     endDrag : function(e){
3865         this.onEndDrag(this.dragData, e);
3866     },
3867
3868     // private
3869     onEndDrag : function(data, e){
3870     },
3871     
3872     // private - pin to cursor
3873     autoOffset : function(x, y) {
3874         this.setDelta(-12, -20);
3875     }    
3876 });/*
3877  * Based on:
3878  * Ext JS Library 1.1.1
3879  * Copyright(c) 2006-2007, Ext JS, LLC.
3880  *
3881  * Originally Released Under LGPL - original licence link has changed is not relivant.
3882  *
3883  * Fork - LGPL
3884  * <script type="text/javascript">
3885  */
3886
3887
3888 /**
3889  * @class Roo.dd.DropTarget
3890  * @extends Roo.dd.DDTarget
3891  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3892  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3893  * @constructor
3894  * @param {String/HTMLElement/Element} el The container element
3895  * @param {Object} config
3896  */
3897 Roo.dd.DropTarget = function(el, config){
3898     this.el = Roo.get(el);
3899     
3900     var listeners = false; ;
3901     if (config && config.listeners) {
3902         listeners= config.listeners;
3903         delete config.listeners;
3904     }
3905     Roo.apply(this, config);
3906     
3907     if(this.containerScroll){
3908         Roo.dd.ScrollManager.register(this.el);
3909     }
3910     this.addEvents( {
3911          /**
3912          * @scope Roo.dd.DropTarget
3913          */
3914          
3915          /**
3916          * @event enter
3917          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3918          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3919          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3920          * 
3921          * IMPORTANT : it should set this.overClass and this.dropAllowed
3922          * 
3923          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3924          * @param {Event} e The event
3925          * @param {Object} data An object containing arbitrary data supplied by the drag source
3926          */
3927         "enter" : true,
3928         
3929          /**
3930          * @event over
3931          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3932          * This method will be called on every mouse movement while the drag source is over the drop target.
3933          * This default implementation simply returns the dropAllowed config value.
3934          * 
3935          * IMPORTANT : it should set this.dropAllowed
3936          * 
3937          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3938          * @param {Event} e The event
3939          * @param {Object} data An object containing arbitrary data supplied by the drag source
3940          
3941          */
3942         "over" : true,
3943         /**
3944          * @event out
3945          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3946          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3947          * overClass (if any) from the drop element.
3948          * 
3949          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3950          * @param {Event} e The event
3951          * @param {Object} data An object containing arbitrary data supplied by the drag source
3952          */
3953          "out" : true,
3954          
3955         /**
3956          * @event drop
3957          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3958          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3959          * implementation that does something to process the drop event and returns true so that the drag source's
3960          * repair action does not run.
3961          * 
3962          * IMPORTANT : it should set this.success
3963          * 
3964          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3965          * @param {Event} e The event
3966          * @param {Object} data An object containing arbitrary data supplied by the drag source
3967         */
3968          "drop" : true
3969     });
3970             
3971      
3972     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3973         this.el.dom, 
3974         this.ddGroup || this.group,
3975         {
3976             isTarget: true,
3977             listeners : listeners || {} 
3978            
3979         
3980         }
3981     );
3982
3983 };
3984
3985 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3986     /**
3987      * @cfg {String} overClass
3988      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3989      */
3990      /**
3991      * @cfg {String} ddGroup
3992      * The drag drop group to handle drop events for
3993      */
3994      
3995     /**
3996      * @cfg {String} dropAllowed
3997      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3998      */
3999     dropAllowed : "x-dd-drop-ok",
4000     /**
4001      * @cfg {String} dropNotAllowed
4002      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4003      */
4004     dropNotAllowed : "x-dd-drop-nodrop",
4005     /**
4006      * @cfg {boolean} success
4007      * set this after drop listener.. 
4008      */
4009     success : false,
4010     /**
4011      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4012      * if the drop point is valid for over/enter..
4013      */
4014     valid : false,
4015     // private
4016     isTarget : true,
4017
4018     // private
4019     isNotifyTarget : true,
4020     
4021     /**
4022      * @hide
4023      */
4024     notifyEnter : function(dd, e, data)
4025     {
4026         this.valid = true;
4027         this.fireEvent('enter', dd, e, data);
4028         if(this.overClass){
4029             this.el.addClass(this.overClass);
4030         }
4031         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4032             this.valid ? this.dropAllowed : this.dropNotAllowed
4033         );
4034     },
4035
4036     /**
4037      * @hide
4038      */
4039     notifyOver : function(dd, e, data)
4040     {
4041         this.valid = true;
4042         this.fireEvent('over', dd, e, data);
4043         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4044             this.valid ? this.dropAllowed : this.dropNotAllowed
4045         );
4046     },
4047
4048     /**
4049      * @hide
4050      */
4051     notifyOut : function(dd, e, data)
4052     {
4053         this.fireEvent('out', dd, e, data);
4054         if(this.overClass){
4055             this.el.removeClass(this.overClass);
4056         }
4057     },
4058
4059     /**
4060      * @hide
4061      */
4062     notifyDrop : function(dd, e, data)
4063     {
4064         this.success = false;
4065         this.fireEvent('drop', dd, e, data);
4066         return this.success;
4067     }
4068 });/*
4069  * Based on:
4070  * Ext JS Library 1.1.1
4071  * Copyright(c) 2006-2007, Ext JS, LLC.
4072  *
4073  * Originally Released Under LGPL - original licence link has changed is not relivant.
4074  *
4075  * Fork - LGPL
4076  * <script type="text/javascript">
4077  */
4078
4079
4080 /**
4081  * @class Roo.dd.DragZone
4082  * @extends Roo.dd.DragSource
4083  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4084  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4085  * @constructor
4086  * @param {String/HTMLElement/Element} el The container element
4087  * @param {Object} config
4088  */
4089 Roo.dd.DragZone = function(el, config){
4090     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4091     if(this.containerScroll){
4092         Roo.dd.ScrollManager.register(this.el);
4093     }
4094 };
4095
4096 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4097     /**
4098      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4099      * for auto scrolling during drag operations.
4100      */
4101     /**
4102      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4103      * method after a failed drop (defaults to "c3daf9" - light blue)
4104      */
4105
4106     /**
4107      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4108      * for a valid target to drag based on the mouse down. Override this method
4109      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4110      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4111      * @param {EventObject} e The mouse down event
4112      * @return {Object} The dragData
4113      */
4114     getDragData : function(e){
4115         return Roo.dd.Registry.getHandleFromEvent(e);
4116     },
4117     
4118     /**
4119      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4120      * this.dragData.ddel
4121      * @param {Number} x The x position of the click on the dragged object
4122      * @param {Number} y The y position of the click on the dragged object
4123      * @return {Boolean} true to continue the drag, false to cancel
4124      */
4125     onInitDrag : function(x, y){
4126         this.proxy.update(this.dragData.ddel.cloneNode(true));
4127         this.onStartDrag(x, y);
4128         return true;
4129     },
4130     
4131     /**
4132      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4133      */
4134     afterRepair : function(){
4135         if(Roo.enableFx){
4136             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4137         }
4138         this.dragging = false;
4139     },
4140
4141     /**
4142      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4143      * the XY of this.dragData.ddel
4144      * @param {EventObject} e The mouse up event
4145      * @return {Array} The xy location (e.g. [100, 200])
4146      */
4147     getRepairXY : function(e){
4148         return Roo.Element.fly(this.dragData.ddel).getXY();  
4149     }
4150 });/*
4151  * Based on:
4152  * Ext JS Library 1.1.1
4153  * Copyright(c) 2006-2007, Ext JS, LLC.
4154  *
4155  * Originally Released Under LGPL - original licence link has changed is not relivant.
4156  *
4157  * Fork - LGPL
4158  * <script type="text/javascript">
4159  */
4160 /**
4161  * @class Roo.dd.DropZone
4162  * @extends Roo.dd.DropTarget
4163  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4164  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4165  * @constructor
4166  * @param {String/HTMLElement/Element} el The container element
4167  * @param {Object} config
4168  */
4169 Roo.dd.DropZone = function(el, config){
4170     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4171 };
4172
4173 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4174     /**
4175      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4176      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4177      * provide your own custom lookup.
4178      * @param {Event} e The event
4179      * @return {Object} data The custom data
4180      */
4181     getTargetFromEvent : function(e){
4182         return Roo.dd.Registry.getTargetFromEvent(e);
4183     },
4184
4185     /**
4186      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4187      * that it has registered.  This method has no default implementation and should be overridden to provide
4188      * node-specific processing if necessary.
4189      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4190      * {@link #getTargetFromEvent} for this node)
4191      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4192      * @param {Event} e The event
4193      * @param {Object} data An object containing arbitrary data supplied by the drag source
4194      */
4195     onNodeEnter : function(n, dd, e, data){
4196         
4197     },
4198
4199     /**
4200      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4201      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4202      * overridden to provide the proper feedback.
4203      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4204      * {@link #getTargetFromEvent} for this node)
4205      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4206      * @param {Event} e The event
4207      * @param {Object} data An object containing arbitrary data supplied by the drag source
4208      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4209      * underlying {@link Roo.dd.StatusProxy} can be updated
4210      */
4211     onNodeOver : function(n, dd, e, data){
4212         return this.dropAllowed;
4213     },
4214
4215     /**
4216      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4217      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4218      * node-specific processing if necessary.
4219      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4220      * {@link #getTargetFromEvent} for this node)
4221      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4222      * @param {Event} e The event
4223      * @param {Object} data An object containing arbitrary data supplied by the drag source
4224      */
4225     onNodeOut : function(n, dd, e, data){
4226         
4227     },
4228
4229     /**
4230      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4231      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4232      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4233      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4234      * {@link #getTargetFromEvent} for this node)
4235      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4236      * @param {Event} e The event
4237      * @param {Object} data An object containing arbitrary data supplied by the drag source
4238      * @return {Boolean} True if the drop was valid, else false
4239      */
4240     onNodeDrop : function(n, dd, e, data){
4241         return false;
4242     },
4243
4244     /**
4245      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4246      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4247      * it should be overridden to provide the proper feedback if necessary.
4248      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4249      * @param {Event} e The event
4250      * @param {Object} data An object containing arbitrary data supplied by the drag source
4251      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4252      * underlying {@link Roo.dd.StatusProxy} can be updated
4253      */
4254     onContainerOver : function(dd, e, data){
4255         return this.dropNotAllowed;
4256     },
4257
4258     /**
4259      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4260      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4261      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4262      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4263      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4264      * @param {Event} e The event
4265      * @param {Object} data An object containing arbitrary data supplied by the drag source
4266      * @return {Boolean} True if the drop was valid, else false
4267      */
4268     onContainerDrop : function(dd, e, data){
4269         return false;
4270     },
4271
4272     /**
4273      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4274      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4275      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4276      * you should override this method and provide a custom implementation.
4277      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4278      * @param {Event} e The event
4279      * @param {Object} data An object containing arbitrary data supplied by the drag source
4280      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4281      * underlying {@link Roo.dd.StatusProxy} can be updated
4282      */
4283     notifyEnter : function(dd, e, data){
4284         return this.dropNotAllowed;
4285     },
4286
4287     /**
4288      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4289      * This method will be called on every mouse movement while the drag source is over the drop zone.
4290      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4291      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4292      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4293      * registered node, it will call {@link #onContainerOver}.
4294      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4295      * @param {Event} e The event
4296      * @param {Object} data An object containing arbitrary data supplied by the drag source
4297      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4298      * underlying {@link Roo.dd.StatusProxy} can be updated
4299      */
4300     notifyOver : function(dd, e, data){
4301         var n = this.getTargetFromEvent(e);
4302         if(!n){ // not over valid drop target
4303             if(this.lastOverNode){
4304                 this.onNodeOut(this.lastOverNode, dd, e, data);
4305                 this.lastOverNode = null;
4306             }
4307             return this.onContainerOver(dd, e, data);
4308         }
4309         if(this.lastOverNode != n){
4310             if(this.lastOverNode){
4311                 this.onNodeOut(this.lastOverNode, dd, e, data);
4312             }
4313             this.onNodeEnter(n, dd, e, data);
4314             this.lastOverNode = n;
4315         }
4316         return this.onNodeOver(n, dd, e, data);
4317     },
4318
4319     /**
4320      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4321      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4322      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4323      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4324      * @param {Event} e The event
4325      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4326      */
4327     notifyOut : function(dd, e, data){
4328         if(this.lastOverNode){
4329             this.onNodeOut(this.lastOverNode, dd, e, data);
4330             this.lastOverNode = null;
4331         }
4332     },
4333
4334     /**
4335      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4336      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4337      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4338      * otherwise it will call {@link #onContainerDrop}.
4339      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4340      * @param {Event} e The event
4341      * @param {Object} data An object containing arbitrary data supplied by the drag source
4342      * @return {Boolean} True if the drop was valid, else false
4343      */
4344     notifyDrop : function(dd, e, data){
4345         if(this.lastOverNode){
4346             this.onNodeOut(this.lastOverNode, dd, e, data);
4347             this.lastOverNode = null;
4348         }
4349         var n = this.getTargetFromEvent(e);
4350         return n ?
4351             this.onNodeDrop(n, dd, e, data) :
4352             this.onContainerDrop(dd, e, data);
4353     },
4354
4355     // private
4356     triggerCacheRefresh : function(){
4357         Roo.dd.DDM.refreshCache(this.groups);
4358     }  
4359 });/*
4360  * Based on:
4361  * Ext JS Library 1.1.1
4362  * Copyright(c) 2006-2007, Ext JS, LLC.
4363  *
4364  * Originally Released Under LGPL - original licence link has changed is not relivant.
4365  *
4366  * Fork - LGPL
4367  * <script type="text/javascript">
4368  */
4369
4370
4371 /**
4372  * @class Roo.data.SortTypes
4373  * @singleton
4374  * Defines the default sorting (casting?) comparison functions used when sorting data.
4375  */
4376 Roo.data.SortTypes = {
4377     /**
4378      * Default sort that does nothing
4379      * @param {Mixed} s The value being converted
4380      * @return {Mixed} The comparison value
4381      */
4382     none : function(s){
4383         return s;
4384     },
4385     
4386     /**
4387      * The regular expression used to strip tags
4388      * @type {RegExp}
4389      * @property
4390      */
4391     stripTagsRE : /<\/?[^>]+>/gi,
4392     
4393     /**
4394      * Strips all HTML tags to sort on text only
4395      * @param {Mixed} s The value being converted
4396      * @return {String} The comparison value
4397      */
4398     asText : function(s){
4399         return String(s).replace(this.stripTagsRE, "");
4400     },
4401     
4402     /**
4403      * Strips all HTML tags to sort on text only - Case insensitive
4404      * @param {Mixed} s The value being converted
4405      * @return {String} The comparison value
4406      */
4407     asUCText : function(s){
4408         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4409     },
4410     
4411     /**
4412      * Case insensitive string
4413      * @param {Mixed} s The value being converted
4414      * @return {String} The comparison value
4415      */
4416     asUCString : function(s) {
4417         return String(s).toUpperCase();
4418     },
4419     
4420     /**
4421      * Date sorting
4422      * @param {Mixed} s The value being converted
4423      * @return {Number} The comparison value
4424      */
4425     asDate : function(s) {
4426         if(!s){
4427             return 0;
4428         }
4429         if(s instanceof Date){
4430             return s.getTime();
4431         }
4432         return Date.parse(String(s));
4433     },
4434     
4435     /**
4436      * Float sorting
4437      * @param {Mixed} s The value being converted
4438      * @return {Float} The comparison value
4439      */
4440     asFloat : function(s) {
4441         var val = parseFloat(String(s).replace(/,/g, ""));
4442         if(isNaN(val)) val = 0;
4443         return val;
4444     },
4445     
4446     /**
4447      * Integer sorting
4448      * @param {Mixed} s The value being converted
4449      * @return {Number} The comparison value
4450      */
4451     asInt : function(s) {
4452         var val = parseInt(String(s).replace(/,/g, ""));
4453         if(isNaN(val)) val = 0;
4454         return val;
4455     }
4456 };/*
4457  * Based on:
4458  * Ext JS Library 1.1.1
4459  * Copyright(c) 2006-2007, Ext JS, LLC.
4460  *
4461  * Originally Released Under LGPL - original licence link has changed is not relivant.
4462  *
4463  * Fork - LGPL
4464  * <script type="text/javascript">
4465  */
4466
4467 /**
4468 * @class Roo.data.Record
4469  * Instances of this class encapsulate both record <em>definition</em> information, and record
4470  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4471  * to access Records cached in an {@link Roo.data.Store} object.<br>
4472  * <p>
4473  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4474  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4475  * objects.<br>
4476  * <p>
4477  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4478  * @constructor
4479  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4480  * {@link #create}. The parameters are the same.
4481  * @param {Array} data An associative Array of data values keyed by the field name.
4482  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4483  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4484  * not specified an integer id is generated.
4485  */
4486 Roo.data.Record = function(data, id){
4487     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4488     this.data = data;
4489 };
4490
4491 /**
4492  * Generate a constructor for a specific record layout.
4493  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4494  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4495  * Each field definition object may contain the following properties: <ul>
4496  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4497  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4498  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4499  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4500  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4501  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4502  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4503  * this may be omitted.</p></li>
4504  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4505  * <ul><li>auto (Default, implies no conversion)</li>
4506  * <li>string</li>
4507  * <li>int</li>
4508  * <li>float</li>
4509  * <li>boolean</li>
4510  * <li>date</li></ul></p></li>
4511  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4512  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4513  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4514  * by the Reader into an object that will be stored in the Record. It is passed the
4515  * following parameters:<ul>
4516  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4517  * </ul></p></li>
4518  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4519  * </ul>
4520  * <br>usage:<br><pre><code>
4521 var TopicRecord = Roo.data.Record.create(
4522     {name: 'title', mapping: 'topic_title'},
4523     {name: 'author', mapping: 'username'},
4524     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4525     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4526     {name: 'lastPoster', mapping: 'user2'},
4527     {name: 'excerpt', mapping: 'post_text'}
4528 );
4529
4530 var myNewRecord = new TopicRecord({
4531     title: 'Do my job please',
4532     author: 'noobie',
4533     totalPosts: 1,
4534     lastPost: new Date(),
4535     lastPoster: 'Animal',
4536     excerpt: 'No way dude!'
4537 });
4538 myStore.add(myNewRecord);
4539 </code></pre>
4540  * @method create
4541  * @static
4542  */
4543 Roo.data.Record.create = function(o){
4544     var f = function(){
4545         f.superclass.constructor.apply(this, arguments);
4546     };
4547     Roo.extend(f, Roo.data.Record);
4548     var p = f.prototype;
4549     p.fields = new Roo.util.MixedCollection(false, function(field){
4550         return field.name;
4551     });
4552     for(var i = 0, len = o.length; i < len; i++){
4553         p.fields.add(new Roo.data.Field(o[i]));
4554     }
4555     f.getField = function(name){
4556         return p.fields.get(name);  
4557     };
4558     return f;
4559 };
4560
4561 Roo.data.Record.AUTO_ID = 1000;
4562 Roo.data.Record.EDIT = 'edit';
4563 Roo.data.Record.REJECT = 'reject';
4564 Roo.data.Record.COMMIT = 'commit';
4565
4566 Roo.data.Record.prototype = {
4567     /**
4568      * Readonly flag - true if this record has been modified.
4569      * @type Boolean
4570      */
4571     dirty : false,
4572     editing : false,
4573     error: null,
4574     modified: null,
4575
4576     // private
4577     join : function(store){
4578         this.store = store;
4579     },
4580
4581     /**
4582      * Set the named field to the specified value.
4583      * @param {String} name The name of the field to set.
4584      * @param {Object} value The value to set the field to.
4585      */
4586     set : function(name, value){
4587         if(this.data[name] == value){
4588             return;
4589         }
4590         this.dirty = true;
4591         if(!this.modified){
4592             this.modified = {};
4593         }
4594         if(typeof this.modified[name] == 'undefined'){
4595             this.modified[name] = this.data[name];
4596         }
4597         this.data[name] = value;
4598         if(!this.editing && this.store){
4599             this.store.afterEdit(this);
4600         }       
4601     },
4602
4603     /**
4604      * Get the value of the named field.
4605      * @param {String} name The name of the field to get the value of.
4606      * @return {Object} The value of the field.
4607      */
4608     get : function(name){
4609         return this.data[name]; 
4610     },
4611
4612     // private
4613     beginEdit : function(){
4614         this.editing = true;
4615         this.modified = {}; 
4616     },
4617
4618     // private
4619     cancelEdit : function(){
4620         this.editing = false;
4621         delete this.modified;
4622     },
4623
4624     // private
4625     endEdit : function(){
4626         this.editing = false;
4627         if(this.dirty && this.store){
4628             this.store.afterEdit(this);
4629         }
4630     },
4631
4632     /**
4633      * Usually called by the {@link Roo.data.Store} which owns the Record.
4634      * Rejects all changes made to the Record since either creation, or the last commit operation.
4635      * Modified fields are reverted to their original values.
4636      * <p>
4637      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4638      * of reject operations.
4639      */
4640     reject : function(){
4641         var m = this.modified;
4642         for(var n in m){
4643             if(typeof m[n] != "function"){
4644                 this.data[n] = m[n];
4645             }
4646         }
4647         this.dirty = false;
4648         delete this.modified;
4649         this.editing = false;
4650         if(this.store){
4651             this.store.afterReject(this);
4652         }
4653     },
4654
4655     /**
4656      * Usually called by the {@link Roo.data.Store} which owns the Record.
4657      * Commits all changes made to the Record since either creation, or the last commit operation.
4658      * <p>
4659      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4660      * of commit operations.
4661      */
4662     commit : function(){
4663         this.dirty = false;
4664         delete this.modified;
4665         this.editing = false;
4666         if(this.store){
4667             this.store.afterCommit(this);
4668         }
4669     },
4670
4671     // private
4672     hasError : function(){
4673         return this.error != null;
4674     },
4675
4676     // private
4677     clearError : function(){
4678         this.error = null;
4679     },
4680
4681     /**
4682      * Creates a copy of this record.
4683      * @param {String} id (optional) A new record id if you don't want to use this record's id
4684      * @return {Record}
4685      */
4686     copy : function(newId) {
4687         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4688     }
4689 };/*
4690  * Based on:
4691  * Ext JS Library 1.1.1
4692  * Copyright(c) 2006-2007, Ext JS, LLC.
4693  *
4694  * Originally Released Under LGPL - original licence link has changed is not relivant.
4695  *
4696  * Fork - LGPL
4697  * <script type="text/javascript">
4698  */
4699
4700
4701
4702 /**
4703  * @class Roo.data.Store
4704  * @extends Roo.util.Observable
4705  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4706  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4707  * <p>
4708  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4709  * has no knowledge of the format of the data returned by the Proxy.<br>
4710  * <p>
4711  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4712  * instances from the data object. These records are cached and made available through accessor functions.
4713  * @constructor
4714  * Creates a new Store.
4715  * @param {Object} config A config object containing the objects needed for the Store to access data,
4716  * and read the data into Records.
4717  */
4718 Roo.data.Store = function(config){
4719     this.data = new Roo.util.MixedCollection(false);
4720     this.data.getKey = function(o){
4721         return o.id;
4722     };
4723     this.baseParams = {};
4724     // private
4725     this.paramNames = {
4726         "start" : "start",
4727         "limit" : "limit",
4728         "sort" : "sort",
4729         "dir" : "dir",
4730         "multisort" : "_multisort"
4731     };
4732
4733     if(config && config.data){
4734         this.inlineData = config.data;
4735         delete config.data;
4736     }
4737
4738     Roo.apply(this, config);
4739     
4740     if(this.reader){ // reader passed
4741         this.reader = Roo.factory(this.reader, Roo.data);
4742         this.reader.xmodule = this.xmodule || false;
4743         if(!this.recordType){
4744             this.recordType = this.reader.recordType;
4745         }
4746         if(this.reader.onMetaChange){
4747             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4748         }
4749     }
4750
4751     if(this.recordType){
4752         this.fields = this.recordType.prototype.fields;
4753     }
4754     this.modified = [];
4755
4756     this.addEvents({
4757         /**
4758          * @event datachanged
4759          * Fires when the data cache has changed, and a widget which is using this Store
4760          * as a Record cache should refresh its view.
4761          * @param {Store} this
4762          */
4763         datachanged : true,
4764         /**
4765          * @event metachange
4766          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4767          * @param {Store} this
4768          * @param {Object} meta The JSON metadata
4769          */
4770         metachange : true,
4771         /**
4772          * @event add
4773          * Fires when Records have been added to the Store
4774          * @param {Store} this
4775          * @param {Roo.data.Record[]} records The array of Records added
4776          * @param {Number} index The index at which the record(s) were added
4777          */
4778         add : true,
4779         /**
4780          * @event remove
4781          * Fires when a Record has been removed from the Store
4782          * @param {Store} this
4783          * @param {Roo.data.Record} record The Record that was removed
4784          * @param {Number} index The index at which the record was removed
4785          */
4786         remove : true,
4787         /**
4788          * @event update
4789          * Fires when a Record has been updated
4790          * @param {Store} this
4791          * @param {Roo.data.Record} record The Record that was updated
4792          * @param {String} operation The update operation being performed.  Value may be one of:
4793          * <pre><code>
4794  Roo.data.Record.EDIT
4795  Roo.data.Record.REJECT
4796  Roo.data.Record.COMMIT
4797          * </code></pre>
4798          */
4799         update : true,
4800         /**
4801          * @event clear
4802          * Fires when the data cache has been cleared.
4803          * @param {Store} this
4804          */
4805         clear : true,
4806         /**
4807          * @event beforeload
4808          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4809          * the load action will be canceled.
4810          * @param {Store} this
4811          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4812          */
4813         beforeload : true,
4814         /**
4815          * @event load
4816          * Fires after a new set of Records has been loaded.
4817          * @param {Store} this
4818          * @param {Roo.data.Record[]} records The Records that were loaded
4819          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4820          */
4821         load : true,
4822         /**
4823          * @event loadexception
4824          * Fires if an exception occurs in the Proxy during loading.
4825          * Called with the signature of the Proxy's "loadexception" event.
4826          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4827          * 
4828          * @param {Proxy} 
4829          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4830          * @param {Object} load options 
4831          * @param {Object} jsonData from your request (normally this contains the Exception)
4832          */
4833         loadexception : true
4834     });
4835     
4836     if(this.proxy){
4837         this.proxy = Roo.factory(this.proxy, Roo.data);
4838         this.proxy.xmodule = this.xmodule || false;
4839         this.relayEvents(this.proxy,  ["loadexception"]);
4840     }
4841     this.sortToggle = {};
4842     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4843
4844     Roo.data.Store.superclass.constructor.call(this);
4845
4846     if(this.inlineData){
4847         this.loadData(this.inlineData);
4848         delete this.inlineData;
4849     }
4850 };
4851 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4852      /**
4853     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4854     * without a remote query - used by combo/forms at present.
4855     */
4856     
4857     /**
4858     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4859     */
4860     /**
4861     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4862     */
4863     /**
4864     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4865     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4866     */
4867     /**
4868     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4869     * on any HTTP request
4870     */
4871     /**
4872     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4873     */
4874     /**
4875     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4876     */
4877     multiSort: false,
4878     /**
4879     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4880     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4881     */
4882     remoteSort : false,
4883
4884     /**
4885     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4886      * loaded or when a record is removed. (defaults to false).
4887     */
4888     pruneModifiedRecords : false,
4889
4890     // private
4891     lastOptions : null,
4892
4893     /**
4894      * Add Records to the Store and fires the add event.
4895      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4896      */
4897     add : function(records){
4898         records = [].concat(records);
4899         for(var i = 0, len = records.length; i < len; i++){
4900             records[i].join(this);
4901         }
4902         var index = this.data.length;
4903         this.data.addAll(records);
4904         this.fireEvent("add", this, records, index);
4905     },
4906
4907     /**
4908      * Remove a Record from the Store and fires the remove event.
4909      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4910      */
4911     remove : function(record){
4912         var index = this.data.indexOf(record);
4913         this.data.removeAt(index);
4914         if(this.pruneModifiedRecords){
4915             this.modified.remove(record);
4916         }
4917         this.fireEvent("remove", this, record, index);
4918     },
4919
4920     /**
4921      * Remove all Records from the Store and fires the clear event.
4922      */
4923     removeAll : function(){
4924         this.data.clear();
4925         if(this.pruneModifiedRecords){
4926             this.modified = [];
4927         }
4928         this.fireEvent("clear", this);
4929     },
4930
4931     /**
4932      * Inserts Records to the Store at the given index and fires the add event.
4933      * @param {Number} index The start index at which to insert the passed Records.
4934      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4935      */
4936     insert : function(index, records){
4937         records = [].concat(records);
4938         for(var i = 0, len = records.length; i < len; i++){
4939             this.data.insert(index, records[i]);
4940             records[i].join(this);
4941         }
4942         this.fireEvent("add", this, records, index);
4943     },
4944
4945     /**
4946      * Get the index within the cache of the passed Record.
4947      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4948      * @return {Number} The index of the passed Record. Returns -1 if not found.
4949      */
4950     indexOf : function(record){
4951         return this.data.indexOf(record);
4952     },
4953
4954     /**
4955      * Get the index within the cache of the Record with the passed id.
4956      * @param {String} id The id of the Record to find.
4957      * @return {Number} The index of the Record. Returns -1 if not found.
4958      */
4959     indexOfId : function(id){
4960         return this.data.indexOfKey(id);
4961     },
4962
4963     /**
4964      * Get the Record with the specified id.
4965      * @param {String} id The id of the Record to find.
4966      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4967      */
4968     getById : function(id){
4969         return this.data.key(id);
4970     },
4971
4972     /**
4973      * Get the Record at the specified index.
4974      * @param {Number} index The index of the Record to find.
4975      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4976      */
4977     getAt : function(index){
4978         return this.data.itemAt(index);
4979     },
4980
4981     /**
4982      * Returns a range of Records between specified indices.
4983      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4984      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4985      * @return {Roo.data.Record[]} An array of Records
4986      */
4987     getRange : function(start, end){
4988         return this.data.getRange(start, end);
4989     },
4990
4991     // private
4992     storeOptions : function(o){
4993         o = Roo.apply({}, o);
4994         delete o.callback;
4995         delete o.scope;
4996         this.lastOptions = o;
4997     },
4998
4999     /**
5000      * Loads the Record cache from the configured Proxy using the configured Reader.
5001      * <p>
5002      * If using remote paging, then the first load call must specify the <em>start</em>
5003      * and <em>limit</em> properties in the options.params property to establish the initial
5004      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5005      * <p>
5006      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5007      * and this call will return before the new data has been loaded. Perform any post-processing
5008      * in a callback function, or in a "load" event handler.</strong>
5009      * <p>
5010      * @param {Object} options An object containing properties which control loading options:<ul>
5011      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5012      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5013      * passed the following arguments:<ul>
5014      * <li>r : Roo.data.Record[]</li>
5015      * <li>options: Options object from the load call</li>
5016      * <li>success: Boolean success indicator</li></ul></li>
5017      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5018      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5019      * </ul>
5020      */
5021     load : function(options){
5022         options = options || {};
5023         if(this.fireEvent("beforeload", this, options) !== false){
5024             this.storeOptions(options);
5025             var p = Roo.apply(options.params || {}, this.baseParams);
5026             // if meta was not loaded from remote source.. try requesting it.
5027             if (!this.reader.metaFromRemote) {
5028                 p._requestMeta = 1;
5029             }
5030             if(this.sortInfo && this.remoteSort){
5031                 var pn = this.paramNames;
5032                 p[pn["sort"]] = this.sortInfo.field;
5033                 p[pn["dir"]] = this.sortInfo.direction;
5034             }
5035             if (this.multiSort) {
5036                 var pn = this.paramNames;
5037                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5038             }
5039             
5040             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5041         }
5042     },
5043
5044     /**
5045      * Reloads the Record cache from the configured Proxy using the configured Reader and
5046      * the options from the last load operation performed.
5047      * @param {Object} options (optional) An object containing properties which may override the options
5048      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5049      * the most recently used options are reused).
5050      */
5051     reload : function(options){
5052         this.load(Roo.applyIf(options||{}, this.lastOptions));
5053     },
5054
5055     // private
5056     // Called as a callback by the Reader during a load operation.
5057     loadRecords : function(o, options, success){
5058         if(!o || success === false){
5059             if(success !== false){
5060                 this.fireEvent("load", this, [], options);
5061             }
5062             if(options.callback){
5063                 options.callback.call(options.scope || this, [], options, false);
5064             }
5065             return;
5066         }
5067         // if data returned failure - throw an exception.
5068         if (o.success === false) {
5069             // show a message if no listener is registered.
5070             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
5071                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
5072             }
5073             // loadmask wil be hooked into this..
5074             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5075             return;
5076         }
5077         var r = o.records, t = o.totalRecords || r.length;
5078         if(!options || options.add !== true){
5079             if(this.pruneModifiedRecords){
5080                 this.modified = [];
5081             }
5082             for(var i = 0, len = r.length; i < len; i++){
5083                 r[i].join(this);
5084             }
5085             if(this.snapshot){
5086                 this.data = this.snapshot;
5087                 delete this.snapshot;
5088             }
5089             this.data.clear();
5090             this.data.addAll(r);
5091             this.totalLength = t;
5092             this.applySort();
5093             this.fireEvent("datachanged", this);
5094         }else{
5095             this.totalLength = Math.max(t, this.data.length+r.length);
5096             this.add(r);
5097         }
5098         this.fireEvent("load", this, r, options);
5099         if(options.callback){
5100             options.callback.call(options.scope || this, r, options, true);
5101         }
5102     },
5103
5104
5105     /**
5106      * Loads data from a passed data block. A Reader which understands the format of the data
5107      * must have been configured in the constructor.
5108      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5109      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5110      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5111      */
5112     loadData : function(o, append){
5113         var r = this.reader.readRecords(o);
5114         this.loadRecords(r, {add: append}, true);
5115     },
5116
5117     /**
5118      * Gets the number of cached records.
5119      * <p>
5120      * <em>If using paging, this may not be the total size of the dataset. If the data object
5121      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5122      * the data set size</em>
5123      */
5124     getCount : function(){
5125         return this.data.length || 0;
5126     },
5127
5128     /**
5129      * Gets the total number of records in the dataset as returned by the server.
5130      * <p>
5131      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5132      * the dataset size</em>
5133      */
5134     getTotalCount : function(){
5135         return this.totalLength || 0;
5136     },
5137
5138     /**
5139      * Returns the sort state of the Store as an object with two properties:
5140      * <pre><code>
5141  field {String} The name of the field by which the Records are sorted
5142  direction {String} The sort order, "ASC" or "DESC"
5143      * </code></pre>
5144      */
5145     getSortState : function(){
5146         return this.sortInfo;
5147     },
5148
5149     // private
5150     applySort : function(){
5151         if(this.sortInfo && !this.remoteSort){
5152             var s = this.sortInfo, f = s.field;
5153             var st = this.fields.get(f).sortType;
5154             var fn = function(r1, r2){
5155                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5156                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5157             };
5158             this.data.sort(s.direction, fn);
5159             if(this.snapshot && this.snapshot != this.data){
5160                 this.snapshot.sort(s.direction, fn);
5161             }
5162         }
5163     },
5164
5165     /**
5166      * Sets the default sort column and order to be used by the next load operation.
5167      * @param {String} fieldName The name of the field to sort by.
5168      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5169      */
5170     setDefaultSort : function(field, dir){
5171         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5172     },
5173
5174     /**
5175      * Sort the Records.
5176      * If remote sorting is used, the sort is performed on the server, and the cache is
5177      * reloaded. If local sorting is used, the cache is sorted internally.
5178      * @param {String} fieldName The name of the field to sort by.
5179      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5180      */
5181     sort : function(fieldName, dir){
5182         var f = this.fields.get(fieldName);
5183         if(!dir){
5184             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5185             
5186             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5187                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5188             }else{
5189                 dir = f.sortDir;
5190             }
5191         }
5192         this.sortToggle[f.name] = dir;
5193         this.sortInfo = {field: f.name, direction: dir};
5194         if(!this.remoteSort){
5195             this.applySort();
5196             this.fireEvent("datachanged", this);
5197         }else{
5198             this.load(this.lastOptions);
5199         }
5200     },
5201
5202     /**
5203      * Calls the specified function for each of the Records in the cache.
5204      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5205      * Returning <em>false</em> aborts and exits the iteration.
5206      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5207      */
5208     each : function(fn, scope){
5209         this.data.each(fn, scope);
5210     },
5211
5212     /**
5213      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5214      * (e.g., during paging).
5215      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5216      */
5217     getModifiedRecords : function(){
5218         return this.modified;
5219     },
5220
5221     // private
5222     createFilterFn : function(property, value, anyMatch){
5223         if(!value.exec){ // not a regex
5224             value = String(value);
5225             if(value.length == 0){
5226                 return false;
5227             }
5228             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5229         }
5230         return function(r){
5231             return value.test(r.data[property]);
5232         };
5233     },
5234
5235     /**
5236      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5237      * @param {String} property A field on your records
5238      * @param {Number} start The record index to start at (defaults to 0)
5239      * @param {Number} end The last record index to include (defaults to length - 1)
5240      * @return {Number} The sum
5241      */
5242     sum : function(property, start, end){
5243         var rs = this.data.items, v = 0;
5244         start = start || 0;
5245         end = (end || end === 0) ? end : rs.length-1;
5246
5247         for(var i = start; i <= end; i++){
5248             v += (rs[i].data[property] || 0);
5249         }
5250         return v;
5251     },
5252
5253     /**
5254      * Filter the records by a specified property.
5255      * @param {String} field A field on your records
5256      * @param {String/RegExp} value Either a string that the field
5257      * should start with or a RegExp to test against the field
5258      * @param {Boolean} anyMatch True to match any part not just the beginning
5259      */
5260     filter : function(property, value, anyMatch){
5261         var fn = this.createFilterFn(property, value, anyMatch);
5262         return fn ? this.filterBy(fn) : this.clearFilter();
5263     },
5264
5265     /**
5266      * Filter by a function. The specified function will be called with each
5267      * record in this data source. If the function returns true the record is included,
5268      * otherwise it is filtered.
5269      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5270      * @param {Object} scope (optional) The scope of the function (defaults to this)
5271      */
5272     filterBy : function(fn, scope){
5273         this.snapshot = this.snapshot || this.data;
5274         this.data = this.queryBy(fn, scope||this);
5275         this.fireEvent("datachanged", this);
5276     },
5277
5278     /**
5279      * Query the records by a specified property.
5280      * @param {String} field A field on your records
5281      * @param {String/RegExp} value Either a string that the field
5282      * should start with or a RegExp to test against the field
5283      * @param {Boolean} anyMatch True to match any part not just the beginning
5284      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5285      */
5286     query : function(property, value, anyMatch){
5287         var fn = this.createFilterFn(property, value, anyMatch);
5288         return fn ? this.queryBy(fn) : this.data.clone();
5289     },
5290
5291     /**
5292      * Query by a function. The specified function will be called with each
5293      * record in this data source. If the function returns true the record is included
5294      * in the results.
5295      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5296      * @param {Object} scope (optional) The scope of the function (defaults to this)
5297       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5298      **/
5299     queryBy : function(fn, scope){
5300         var data = this.snapshot || this.data;
5301         return data.filterBy(fn, scope||this);
5302     },
5303
5304     /**
5305      * Collects unique values for a particular dataIndex from this store.
5306      * @param {String} dataIndex The property to collect
5307      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5308      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5309      * @return {Array} An array of the unique values
5310      **/
5311     collect : function(dataIndex, allowNull, bypassFilter){
5312         var d = (bypassFilter === true && this.snapshot) ?
5313                 this.snapshot.items : this.data.items;
5314         var v, sv, r = [], l = {};
5315         for(var i = 0, len = d.length; i < len; i++){
5316             v = d[i].data[dataIndex];
5317             sv = String(v);
5318             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5319                 l[sv] = true;
5320                 r[r.length] = v;
5321             }
5322         }
5323         return r;
5324     },
5325
5326     /**
5327      * Revert to a view of the Record cache with no filtering applied.
5328      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5329      */
5330     clearFilter : function(suppressEvent){
5331         if(this.snapshot && this.snapshot != this.data){
5332             this.data = this.snapshot;
5333             delete this.snapshot;
5334             if(suppressEvent !== true){
5335                 this.fireEvent("datachanged", this);
5336             }
5337         }
5338     },
5339
5340     // private
5341     afterEdit : function(record){
5342         if(this.modified.indexOf(record) == -1){
5343             this.modified.push(record);
5344         }
5345         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5346     },
5347     
5348     // private
5349     afterReject : function(record){
5350         this.modified.remove(record);
5351         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5352     },
5353
5354     // private
5355     afterCommit : function(record){
5356         this.modified.remove(record);
5357         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5358     },
5359
5360     /**
5361      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5362      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5363      */
5364     commitChanges : function(){
5365         var m = this.modified.slice(0);
5366         this.modified = [];
5367         for(var i = 0, len = m.length; i < len; i++){
5368             m[i].commit();
5369         }
5370     },
5371
5372     /**
5373      * Cancel outstanding changes on all changed records.
5374      */
5375     rejectChanges : function(){
5376         var m = this.modified.slice(0);
5377         this.modified = [];
5378         for(var i = 0, len = m.length; i < len; i++){
5379             m[i].reject();
5380         }
5381     },
5382
5383     onMetaChange : function(meta, rtype, o){
5384         this.recordType = rtype;
5385         this.fields = rtype.prototype.fields;
5386         delete this.snapshot;
5387         this.sortInfo = meta.sortInfo || this.sortInfo;
5388         this.modified = [];
5389         this.fireEvent('metachange', this, this.reader.meta);
5390     }
5391 });/*
5392  * Based on:
5393  * Ext JS Library 1.1.1
5394  * Copyright(c) 2006-2007, Ext JS, LLC.
5395  *
5396  * Originally Released Under LGPL - original licence link has changed is not relivant.
5397  *
5398  * Fork - LGPL
5399  * <script type="text/javascript">
5400  */
5401
5402 /**
5403  * @class Roo.data.SimpleStore
5404  * @extends Roo.data.Store
5405  * Small helper class to make creating Stores from Array data easier.
5406  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5407  * @cfg {Array} fields An array of field definition objects, or field name strings.
5408  * @cfg {Array} data The multi-dimensional array of data
5409  * @constructor
5410  * @param {Object} config
5411  */
5412 Roo.data.SimpleStore = function(config){
5413     Roo.data.SimpleStore.superclass.constructor.call(this, {
5414         isLocal : true,
5415         reader: new Roo.data.ArrayReader({
5416                 id: config.id
5417             },
5418             Roo.data.Record.create(config.fields)
5419         ),
5420         proxy : new Roo.data.MemoryProxy(config.data)
5421     });
5422     this.load();
5423 };
5424 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5425  * Based on:
5426  * Ext JS Library 1.1.1
5427  * Copyright(c) 2006-2007, Ext JS, LLC.
5428  *
5429  * Originally Released Under LGPL - original licence link has changed is not relivant.
5430  *
5431  * Fork - LGPL
5432  * <script type="text/javascript">
5433  */
5434
5435 /**
5436 /**
5437  * @extends Roo.data.Store
5438  * @class Roo.data.JsonStore
5439  * Small helper class to make creating Stores for JSON data easier. <br/>
5440 <pre><code>
5441 var store = new Roo.data.JsonStore({
5442     url: 'get-images.php',
5443     root: 'images',
5444     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5445 });
5446 </code></pre>
5447  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5448  * JsonReader and HttpProxy (unless inline data is provided).</b>
5449  * @cfg {Array} fields An array of field definition objects, or field name strings.
5450  * @constructor
5451  * @param {Object} config
5452  */
5453 Roo.data.JsonStore = function(c){
5454     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5455         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5456         reader: new Roo.data.JsonReader(c, c.fields)
5457     }));
5458 };
5459 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5460  * Based on:
5461  * Ext JS Library 1.1.1
5462  * Copyright(c) 2006-2007, Ext JS, LLC.
5463  *
5464  * Originally Released Under LGPL - original licence link has changed is not relivant.
5465  *
5466  * Fork - LGPL
5467  * <script type="text/javascript">
5468  */
5469
5470  
5471 Roo.data.Field = function(config){
5472     if(typeof config == "string"){
5473         config = {name: config};
5474     }
5475     Roo.apply(this, config);
5476     
5477     if(!this.type){
5478         this.type = "auto";
5479     }
5480     
5481     var st = Roo.data.SortTypes;
5482     // named sortTypes are supported, here we look them up
5483     if(typeof this.sortType == "string"){
5484         this.sortType = st[this.sortType];
5485     }
5486     
5487     // set default sortType for strings and dates
5488     if(!this.sortType){
5489         switch(this.type){
5490             case "string":
5491                 this.sortType = st.asUCString;
5492                 break;
5493             case "date":
5494                 this.sortType = st.asDate;
5495                 break;
5496             default:
5497                 this.sortType = st.none;
5498         }
5499     }
5500
5501     // define once
5502     var stripRe = /[\$,%]/g;
5503
5504     // prebuilt conversion function for this field, instead of
5505     // switching every time we're reading a value
5506     if(!this.convert){
5507         var cv, dateFormat = this.dateFormat;
5508         switch(this.type){
5509             case "":
5510             case "auto":
5511             case undefined:
5512                 cv = function(v){ return v; };
5513                 break;
5514             case "string":
5515                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5516                 break;
5517             case "int":
5518                 cv = function(v){
5519                     return v !== undefined && v !== null && v !== '' ?
5520                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5521                     };
5522                 break;
5523             case "float":
5524                 cv = function(v){
5525                     return v !== undefined && v !== null && v !== '' ?
5526                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5527                     };
5528                 break;
5529             case "bool":
5530             case "boolean":
5531                 cv = function(v){ return v === true || v === "true" || v == 1; };
5532                 break;
5533             case "date":
5534                 cv = function(v){
5535                     if(!v){
5536                         return '';
5537                     }
5538                     if(v instanceof Date){
5539                         return v;
5540                     }
5541                     if(dateFormat){
5542                         if(dateFormat == "timestamp"){
5543                             return new Date(v*1000);
5544                         }
5545                         return Date.parseDate(v, dateFormat);
5546                     }
5547                     var parsed = Date.parse(v);
5548                     return parsed ? new Date(parsed) : null;
5549                 };
5550              break;
5551             
5552         }
5553         this.convert = cv;
5554     }
5555 };
5556
5557 Roo.data.Field.prototype = {
5558     dateFormat: null,
5559     defaultValue: "",
5560     mapping: null,
5561     sortType : null,
5562     sortDir : "ASC"
5563 };/*
5564  * Based on:
5565  * Ext JS Library 1.1.1
5566  * Copyright(c) 2006-2007, Ext JS, LLC.
5567  *
5568  * Originally Released Under LGPL - original licence link has changed is not relivant.
5569  *
5570  * Fork - LGPL
5571  * <script type="text/javascript">
5572  */
5573  
5574 // Base class for reading structured data from a data source.  This class is intended to be
5575 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5576
5577 /**
5578  * @class Roo.data.DataReader
5579  * Base class for reading structured data from a data source.  This class is intended to be
5580  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5581  */
5582
5583 Roo.data.DataReader = function(meta, recordType){
5584     
5585     this.meta = meta;
5586     
5587     this.recordType = recordType instanceof Array ? 
5588         Roo.data.Record.create(recordType) : recordType;
5589 };
5590
5591 Roo.data.DataReader.prototype = {
5592      /**
5593      * Create an empty record
5594      * @param {Object} data (optional) - overlay some values
5595      * @return {Roo.data.Record} record created.
5596      */
5597     newRow :  function(d) {
5598         var da =  {};
5599         this.recordType.prototype.fields.each(function(c) {
5600             switch( c.type) {
5601                 case 'int' : da[c.name] = 0; break;
5602                 case 'date' : da[c.name] = new Date(); break;
5603                 case 'float' : da[c.name] = 0.0; break;
5604                 case 'boolean' : da[c.name] = false; break;
5605                 default : da[c.name] = ""; break;
5606             }
5607             
5608         });
5609         return new this.recordType(Roo.apply(da, d));
5610     }
5611     
5612 };/*
5613  * Based on:
5614  * Ext JS Library 1.1.1
5615  * Copyright(c) 2006-2007, Ext JS, LLC.
5616  *
5617  * Originally Released Under LGPL - original licence link has changed is not relivant.
5618  *
5619  * Fork - LGPL
5620  * <script type="text/javascript">
5621  */
5622
5623 /**
5624  * @class Roo.data.DataProxy
5625  * @extends Roo.data.Observable
5626  * This class is an abstract base class for implementations which provide retrieval of
5627  * unformatted data objects.<br>
5628  * <p>
5629  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5630  * (of the appropriate type which knows how to parse the data object) to provide a block of
5631  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5632  * <p>
5633  * Custom implementations must implement the load method as described in
5634  * {@link Roo.data.HttpProxy#load}.
5635  */
5636 Roo.data.DataProxy = function(){
5637     this.addEvents({
5638         /**
5639          * @event beforeload
5640          * Fires before a network request is made to retrieve a data object.
5641          * @param {Object} This DataProxy object.
5642          * @param {Object} params The params parameter to the load function.
5643          */
5644         beforeload : true,
5645         /**
5646          * @event load
5647          * Fires before the load method's callback is called.
5648          * @param {Object} This DataProxy object.
5649          * @param {Object} o The data object.
5650          * @param {Object} arg The callback argument object passed to the load function.
5651          */
5652         load : true,
5653         /**
5654          * @event loadexception
5655          * Fires if an Exception occurs during data retrieval.
5656          * @param {Object} This DataProxy object.
5657          * @param {Object} o The data object.
5658          * @param {Object} arg The callback argument object passed to the load function.
5659          * @param {Object} e The Exception.
5660          */
5661         loadexception : true
5662     });
5663     Roo.data.DataProxy.superclass.constructor.call(this);
5664 };
5665
5666 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5667
5668     /**
5669      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5670      */
5671 /*
5672  * Based on:
5673  * Ext JS Library 1.1.1
5674  * Copyright(c) 2006-2007, Ext JS, LLC.
5675  *
5676  * Originally Released Under LGPL - original licence link has changed is not relivant.
5677  *
5678  * Fork - LGPL
5679  * <script type="text/javascript">
5680  */
5681 /**
5682  * @class Roo.data.MemoryProxy
5683  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5684  * to the Reader when its load method is called.
5685  * @constructor
5686  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5687  */
5688 Roo.data.MemoryProxy = function(data){
5689     if (data.data) {
5690         data = data.data;
5691     }
5692     Roo.data.MemoryProxy.superclass.constructor.call(this);
5693     this.data = data;
5694 };
5695
5696 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5697     /**
5698      * Load data from the requested source (in this case an in-memory
5699      * data object passed to the constructor), read the data object into
5700      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5701      * process that block using the passed callback.
5702      * @param {Object} params This parameter is not used by the MemoryProxy class.
5703      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5704      * object into a block of Roo.data.Records.
5705      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5706      * The function must be passed <ul>
5707      * <li>The Record block object</li>
5708      * <li>The "arg" argument from the load function</li>
5709      * <li>A boolean success indicator</li>
5710      * </ul>
5711      * @param {Object} scope The scope in which to call the callback
5712      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5713      */
5714     load : function(params, reader, callback, scope, arg){
5715         params = params || {};
5716         var result;
5717         try {
5718             result = reader.readRecords(this.data);
5719         }catch(e){
5720             this.fireEvent("loadexception", this, arg, null, e);
5721             callback.call(scope, null, arg, false);
5722             return;
5723         }
5724         callback.call(scope, result, arg, true);
5725     },
5726     
5727     // private
5728     update : function(params, records){
5729         
5730     }
5731 });/*
5732  * Based on:
5733  * Ext JS Library 1.1.1
5734  * Copyright(c) 2006-2007, Ext JS, LLC.
5735  *
5736  * Originally Released Under LGPL - original licence link has changed is not relivant.
5737  *
5738  * Fork - LGPL
5739  * <script type="text/javascript">
5740  */
5741 /**
5742  * @class Roo.data.HttpProxy
5743  * @extends Roo.data.DataProxy
5744  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5745  * configured to reference a certain URL.<br><br>
5746  * <p>
5747  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5748  * from which the running page was served.<br><br>
5749  * <p>
5750  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5751  * <p>
5752  * Be aware that to enable the browser to parse an XML document, the server must set
5753  * the Content-Type header in the HTTP response to "text/xml".
5754  * @constructor
5755  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5756  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5757  * will be used to make the request.
5758  */
5759 Roo.data.HttpProxy = function(conn){
5760     Roo.data.HttpProxy.superclass.constructor.call(this);
5761     // is conn a conn config or a real conn?
5762     this.conn = conn;
5763     this.useAjax = !conn || !conn.events;
5764   
5765 };
5766
5767 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5768     // thse are take from connection...
5769     
5770     /**
5771      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5772      */
5773     /**
5774      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5775      * extra parameters to each request made by this object. (defaults to undefined)
5776      */
5777     /**
5778      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5779      *  to each request made by this object. (defaults to undefined)
5780      */
5781     /**
5782      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
5783      */
5784     /**
5785      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5786      */
5787      /**
5788      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5789      * @type Boolean
5790      */
5791   
5792
5793     /**
5794      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5795      * @type Boolean
5796      */
5797     /**
5798      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5799      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5800      * a finer-grained basis than the DataProxy events.
5801      */
5802     getConnection : function(){
5803         return this.useAjax ? Roo.Ajax : this.conn;
5804     },
5805
5806     /**
5807      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5808      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5809      * process that block using the passed callback.
5810      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5811      * for the request to the remote server.
5812      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5813      * object into a block of Roo.data.Records.
5814      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5815      * The function must be passed <ul>
5816      * <li>The Record block object</li>
5817      * <li>The "arg" argument from the load function</li>
5818      * <li>A boolean success indicator</li>
5819      * </ul>
5820      * @param {Object} scope The scope in which to call the callback
5821      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5822      */
5823     load : function(params, reader, callback, scope, arg){
5824         if(this.fireEvent("beforeload", this, params) !== false){
5825             var  o = {
5826                 params : params || {},
5827                 request: {
5828                     callback : callback,
5829                     scope : scope,
5830                     arg : arg
5831                 },
5832                 reader: reader,
5833                 callback : this.loadResponse,
5834                 scope: this
5835             };
5836             if(this.useAjax){
5837                 Roo.applyIf(o, this.conn);
5838                 if(this.activeRequest){
5839                     Roo.Ajax.abort(this.activeRequest);
5840                 }
5841                 this.activeRequest = Roo.Ajax.request(o);
5842             }else{
5843                 this.conn.request(o);
5844             }
5845         }else{
5846             callback.call(scope||this, null, arg, false);
5847         }
5848     },
5849
5850     // private
5851     loadResponse : function(o, success, response){
5852         delete this.activeRequest;
5853         if(!success){
5854             this.fireEvent("loadexception", this, o, response);
5855             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5856             return;
5857         }
5858         var result;
5859         try {
5860             result = o.reader.read(response);
5861         }catch(e){
5862             this.fireEvent("loadexception", this, o, response, e);
5863             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5864             return;
5865         }
5866         
5867         this.fireEvent("load", this, o, o.request.arg);
5868         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5869     },
5870
5871     // private
5872     update : function(dataSet){
5873
5874     },
5875
5876     // private
5877     updateResponse : function(dataSet){
5878
5879     }
5880 });/*
5881  * Based on:
5882  * Ext JS Library 1.1.1
5883  * Copyright(c) 2006-2007, Ext JS, LLC.
5884  *
5885  * Originally Released Under LGPL - original licence link has changed is not relivant.
5886  *
5887  * Fork - LGPL
5888  * <script type="text/javascript">
5889  */
5890
5891 /**
5892  * @class Roo.data.ScriptTagProxy
5893  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5894  * other than the originating domain of the running page.<br><br>
5895  * <p>
5896  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
5897  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5898  * <p>
5899  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5900  * source code that is used as the source inside a &lt;script> tag.<br><br>
5901  * <p>
5902  * In order for the browser to process the returned data, the server must wrap the data object
5903  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5904  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5905  * depending on whether the callback name was passed:
5906  * <p>
5907  * <pre><code>
5908 boolean scriptTag = false;
5909 String cb = request.getParameter("callback");
5910 if (cb != null) {
5911     scriptTag = true;
5912     response.setContentType("text/javascript");
5913 } else {
5914     response.setContentType("application/x-json");
5915 }
5916 Writer out = response.getWriter();
5917 if (scriptTag) {
5918     out.write(cb + "(");
5919 }
5920 out.print(dataBlock.toJsonString());
5921 if (scriptTag) {
5922     out.write(");");
5923 }
5924 </pre></code>
5925  *
5926  * @constructor
5927  * @param {Object} config A configuration object.
5928  */
5929 Roo.data.ScriptTagProxy = function(config){
5930     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5931     Roo.apply(this, config);
5932     this.head = document.getElementsByTagName("head")[0];
5933 };
5934
5935 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5936
5937 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5938     /**
5939      * @cfg {String} url The URL from which to request the data object.
5940      */
5941     /**
5942      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5943      */
5944     timeout : 30000,
5945     /**
5946      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5947      * the server the name of the callback function set up by the load call to process the returned data object.
5948      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5949      * javascript output which calls this named function passing the data object as its only parameter.
5950      */
5951     callbackParam : "callback",
5952     /**
5953      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5954      * name to the request.
5955      */
5956     nocache : true,
5957
5958     /**
5959      * Load data from the configured URL, read the data object into
5960      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5961      * process that block using the passed callback.
5962      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5963      * for the request to the remote server.
5964      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5965      * object into a block of Roo.data.Records.
5966      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5967      * The function must be passed <ul>
5968      * <li>The Record block object</li>
5969      * <li>The "arg" argument from the load function</li>
5970      * <li>A boolean success indicator</li>
5971      * </ul>
5972      * @param {Object} scope The scope in which to call the callback
5973      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5974      */
5975     load : function(params, reader, callback, scope, arg){
5976         if(this.fireEvent("beforeload", this, params) !== false){
5977
5978             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5979
5980             var url = this.url;
5981             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5982             if(this.nocache){
5983                 url += "&_dc=" + (new Date().getTime());
5984             }
5985             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5986             var trans = {
5987                 id : transId,
5988                 cb : "stcCallback"+transId,
5989                 scriptId : "stcScript"+transId,
5990                 params : params,
5991                 arg : arg,
5992                 url : url,
5993                 callback : callback,
5994                 scope : scope,
5995                 reader : reader
5996             };
5997             var conn = this;
5998
5999             window[trans.cb] = function(o){
6000                 conn.handleResponse(o, trans);
6001             };
6002
6003             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6004
6005             if(this.autoAbort !== false){
6006                 this.abort();
6007             }
6008
6009             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6010
6011             var script = document.createElement("script");
6012             script.setAttribute("src", url);
6013             script.setAttribute("type", "text/javascript");
6014             script.setAttribute("id", trans.scriptId);
6015             this.head.appendChild(script);
6016
6017             this.trans = trans;
6018         }else{
6019             callback.call(scope||this, null, arg, false);
6020         }
6021     },
6022
6023     // private
6024     isLoading : function(){
6025         return this.trans ? true : false;
6026     },
6027
6028     /**
6029      * Abort the current server request.
6030      */
6031     abort : function(){
6032         if(this.isLoading()){
6033             this.destroyTrans(this.trans);
6034         }
6035     },
6036
6037     // private
6038     destroyTrans : function(trans, isLoaded){
6039         this.head.removeChild(document.getElementById(trans.scriptId));
6040         clearTimeout(trans.timeoutId);
6041         if(isLoaded){
6042             window[trans.cb] = undefined;
6043             try{
6044                 delete window[trans.cb];
6045             }catch(e){}
6046         }else{
6047             // if hasn't been loaded, wait for load to remove it to prevent script error
6048             window[trans.cb] = function(){
6049                 window[trans.cb] = undefined;
6050                 try{
6051                     delete window[trans.cb];
6052                 }catch(e){}
6053             };
6054         }
6055     },
6056
6057     // private
6058     handleResponse : function(o, trans){
6059         this.trans = false;
6060         this.destroyTrans(trans, true);
6061         var result;
6062         try {
6063             result = trans.reader.readRecords(o);
6064         }catch(e){
6065             this.fireEvent("loadexception", this, o, trans.arg, e);
6066             trans.callback.call(trans.scope||window, null, trans.arg, false);
6067             return;
6068         }
6069         this.fireEvent("load", this, o, trans.arg);
6070         trans.callback.call(trans.scope||window, result, trans.arg, true);
6071     },
6072
6073     // private
6074     handleFailure : function(trans){
6075         this.trans = false;
6076         this.destroyTrans(trans, false);
6077         this.fireEvent("loadexception", this, null, trans.arg);
6078         trans.callback.call(trans.scope||window, null, trans.arg, false);
6079     }
6080 });/*
6081  * Based on:
6082  * Ext JS Library 1.1.1
6083  * Copyright(c) 2006-2007, Ext JS, LLC.
6084  *
6085  * Originally Released Under LGPL - original licence link has changed is not relivant.
6086  *
6087  * Fork - LGPL
6088  * <script type="text/javascript">
6089  */
6090
6091 /**
6092  * @class Roo.data.JsonReader
6093  * @extends Roo.data.DataReader
6094  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6095  * based on mappings in a provided Roo.data.Record constructor.
6096  * 
6097  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6098  * in the reply previously. 
6099  * 
6100  * <p>
6101  * Example code:
6102  * <pre><code>
6103 var RecordDef = Roo.data.Record.create([
6104     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6105     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6106 ]);
6107 var myReader = new Roo.data.JsonReader({
6108     totalProperty: "results",    // The property which contains the total dataset size (optional)
6109     root: "rows",                // The property which contains an Array of row objects
6110     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6111 }, RecordDef);
6112 </code></pre>
6113  * <p>
6114  * This would consume a JSON file like this:
6115  * <pre><code>
6116 { 'results': 2, 'rows': [
6117     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6118     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6119 }
6120 </code></pre>
6121  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6122  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6123  * paged from the remote server.
6124  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6125  * @cfg {String} root name of the property which contains the Array of row objects.
6126  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6127  * @constructor
6128  * Create a new JsonReader
6129  * @param {Object} meta Metadata configuration options
6130  * @param {Object} recordType Either an Array of field definition objects,
6131  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6132  */
6133 Roo.data.JsonReader = function(meta, recordType){
6134     
6135     meta = meta || {};
6136     // set some defaults:
6137     Roo.applyIf(meta, {
6138         totalProperty: 'total',
6139         successProperty : 'success',
6140         root : 'data',
6141         id : 'id'
6142     });
6143     
6144     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6145 };
6146 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6147     
6148     /**
6149      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6150      * Used by Store query builder to append _requestMeta to params.
6151      * 
6152      */
6153     metaFromRemote : false,
6154     /**
6155      * This method is only used by a DataProxy which has retrieved data from a remote server.
6156      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6157      * @return {Object} data A data block which is used by an Roo.data.Store object as
6158      * a cache of Roo.data.Records.
6159      */
6160     read : function(response){
6161         var json = response.responseText;
6162        
6163         var o = /* eval:var:o */ eval("("+json+")");
6164         if(!o) {
6165             throw {message: "JsonReader.read: Json object not found"};
6166         }
6167         
6168         if(o.metaData){
6169             
6170             delete this.ef;
6171             this.metaFromRemote = true;
6172             this.meta = o.metaData;
6173             this.recordType = Roo.data.Record.create(o.metaData.fields);
6174             this.onMetaChange(this.meta, this.recordType, o);
6175         }
6176         return this.readRecords(o);
6177     },
6178
6179     // private function a store will implement
6180     onMetaChange : function(meta, recordType, o){
6181
6182     },
6183
6184     /**
6185          * @ignore
6186          */
6187     simpleAccess: function(obj, subsc) {
6188         return obj[subsc];
6189     },
6190
6191         /**
6192          * @ignore
6193          */
6194     getJsonAccessor: function(){
6195         var re = /[\[\.]/;
6196         return function(expr) {
6197             try {
6198                 return(re.test(expr))
6199                     ? new Function("obj", "return obj." + expr)
6200                     : function(obj){
6201                         return obj[expr];
6202                     };
6203             } catch(e){}
6204             return Roo.emptyFn;
6205         };
6206     }(),
6207
6208     /**
6209      * Create a data block containing Roo.data.Records from an XML document.
6210      * @param {Object} o An object which contains an Array of row objects in the property specified
6211      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6212      * which contains the total size of the dataset.
6213      * @return {Object} data A data block which is used by an Roo.data.Store object as
6214      * a cache of Roo.data.Records.
6215      */
6216     readRecords : function(o){
6217         /**
6218          * After any data loads, the raw JSON data is available for further custom processing.
6219          * @type Object
6220          */
6221         this.jsonData = o;
6222         var s = this.meta, Record = this.recordType,
6223             f = Record.prototype.fields, fi = f.items, fl = f.length;
6224
6225 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6226         if (!this.ef) {
6227             if(s.totalProperty) {
6228                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6229                 }
6230                 if(s.successProperty) {
6231                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6232                 }
6233                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6234                 if (s.id) {
6235                         var g = this.getJsonAccessor(s.id);
6236                         this.getId = function(rec) {
6237                                 var r = g(rec);
6238                                 return (r === undefined || r === "") ? null : r;
6239                         };
6240                 } else {
6241                         this.getId = function(){return null;};
6242                 }
6243             this.ef = [];
6244             for(var jj = 0; jj < fl; jj++){
6245                 f = fi[jj];
6246                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6247                 this.ef[jj] = this.getJsonAccessor(map);
6248             }
6249         }
6250
6251         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6252         if(s.totalProperty){
6253             var vt = parseInt(this.getTotal(o), 10);
6254             if(!isNaN(vt)){
6255                 totalRecords = vt;
6256             }
6257         }
6258         if(s.successProperty){
6259             var vs = this.getSuccess(o);
6260             if(vs === false || vs === 'false'){
6261                 success = false;
6262             }
6263         }
6264         var records = [];
6265             for(var i = 0; i < c; i++){
6266                     var n = root[i];
6267                 var values = {};
6268                 var id = this.getId(n);
6269                 for(var j = 0; j < fl; j++){
6270                     f = fi[j];
6271                 var v = this.ef[j](n);
6272                 if (!f.convert) {
6273                     Roo.log('missing convert for ' + f.name);
6274                     Roo.log(f);
6275                     continue;
6276                 }
6277                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6278                 }
6279                 var record = new Record(values, id);
6280                 record.json = n;
6281                 records[i] = record;
6282             }
6283             return {
6284                 success : success,
6285                 records : records,
6286                 totalRecords : totalRecords
6287             };
6288     }
6289 });/*
6290  * Based on:
6291  * Ext JS Library 1.1.1
6292  * Copyright(c) 2006-2007, Ext JS, LLC.
6293  *
6294  * Originally Released Under LGPL - original licence link has changed is not relivant.
6295  *
6296  * Fork - LGPL
6297  * <script type="text/javascript">
6298  */
6299
6300 /**
6301  * @class Roo.data.XmlReader
6302  * @extends Roo.data.DataReader
6303  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6304  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6305  * <p>
6306  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6307  * header in the HTTP response must be set to "text/xml".</em>
6308  * <p>
6309  * Example code:
6310  * <pre><code>
6311 var RecordDef = Roo.data.Record.create([
6312    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6313    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6314 ]);
6315 var myReader = new Roo.data.XmlReader({
6316    totalRecords: "results", // The element which contains the total dataset size (optional)
6317    record: "row",           // The repeated element which contains row information
6318    id: "id"                 // The element within the row that provides an ID for the record (optional)
6319 }, RecordDef);
6320 </code></pre>
6321  * <p>
6322  * This would consume an XML file like this:
6323  * <pre><code>
6324 &lt;?xml?>
6325 &lt;dataset>
6326  &lt;results>2&lt;/results>
6327  &lt;row>
6328    &lt;id>1&lt;/id>
6329    &lt;name>Bill&lt;/name>
6330    &lt;occupation>Gardener&lt;/occupation>
6331  &lt;/row>
6332  &lt;row>
6333    &lt;id>2&lt;/id>
6334    &lt;name>Ben&lt;/name>
6335    &lt;occupation>Horticulturalist&lt;/occupation>
6336  &lt;/row>
6337 &lt;/dataset>
6338 </code></pre>
6339  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6340  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6341  * paged from the remote server.
6342  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6343  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6344  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6345  * a record identifier value.
6346  * @constructor
6347  * Create a new XmlReader
6348  * @param {Object} meta Metadata configuration options
6349  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6350  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6351  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6352  */
6353 Roo.data.XmlReader = function(meta, recordType){
6354     meta = meta || {};
6355     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6356 };
6357 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6358     /**
6359      * This method is only used by a DataProxy which has retrieved data from a remote server.
6360          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6361          * to contain a method called 'responseXML' that returns an XML document object.
6362      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6363      * a cache of Roo.data.Records.
6364      */
6365     read : function(response){
6366         var doc = response.responseXML;
6367         if(!doc) {
6368             throw {message: "XmlReader.read: XML Document not available"};
6369         }
6370         return this.readRecords(doc);
6371     },
6372
6373     /**
6374      * Create a data block containing Roo.data.Records from an XML document.
6375          * @param {Object} doc A parsed XML document.
6376      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6377      * a cache of Roo.data.Records.
6378      */
6379     readRecords : function(doc){
6380         /**
6381          * After any data loads/reads, the raw XML Document is available for further custom processing.
6382          * @type XMLDocument
6383          */
6384         this.xmlData = doc;
6385         var root = doc.documentElement || doc;
6386         var q = Roo.DomQuery;
6387         var recordType = this.recordType, fields = recordType.prototype.fields;
6388         var sid = this.meta.id;
6389         var totalRecords = 0, success = true;
6390         if(this.meta.totalRecords){
6391             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6392         }
6393         
6394         if(this.meta.success){
6395             var sv = q.selectValue(this.meta.success, root, true);
6396             success = sv !== false && sv !== 'false';
6397         }
6398         var records = [];
6399         var ns = q.select(this.meta.record, root);
6400         for(var i = 0, len = ns.length; i < len; i++) {
6401                 var n = ns[i];
6402                 var values = {};
6403                 var id = sid ? q.selectValue(sid, n) : undefined;
6404                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6405                     var f = fields.items[j];
6406                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6407                     v = f.convert(v);
6408                     values[f.name] = v;
6409                 }
6410                 var record = new recordType(values, id);
6411                 record.node = n;
6412                 records[records.length] = record;
6413             }
6414
6415             return {
6416                 success : success,
6417                 records : records,
6418                 totalRecords : totalRecords || records.length
6419             };
6420     }
6421 });/*
6422  * Based on:
6423  * Ext JS Library 1.1.1
6424  * Copyright(c) 2006-2007, Ext JS, LLC.
6425  *
6426  * Originally Released Under LGPL - original licence link has changed is not relivant.
6427  *
6428  * Fork - LGPL
6429  * <script type="text/javascript">
6430  */
6431
6432 /**
6433  * @class Roo.data.ArrayReader
6434  * @extends Roo.data.DataReader
6435  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6436  * Each element of that Array represents a row of data fields. The
6437  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6438  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6439  * <p>
6440  * Example code:.
6441  * <pre><code>
6442 var RecordDef = Roo.data.Record.create([
6443     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6444     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6445 ]);
6446 var myReader = new Roo.data.ArrayReader({
6447     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6448 }, RecordDef);
6449 </code></pre>
6450  * <p>
6451  * This would consume an Array like this:
6452  * <pre><code>
6453 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6454   </code></pre>
6455  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6456  * @constructor
6457  * Create a new JsonReader
6458  * @param {Object} meta Metadata configuration options.
6459  * @param {Object} recordType Either an Array of field definition objects
6460  * as specified to {@link Roo.data.Record#create},
6461  * or an {@link Roo.data.Record} object
6462  * created using {@link Roo.data.Record#create}.
6463  */
6464 Roo.data.ArrayReader = function(meta, recordType){
6465     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6466 };
6467
6468 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6469     /**
6470      * Create a data block containing Roo.data.Records from an XML document.
6471      * @param {Object} o An Array of row objects which represents the dataset.
6472      * @return {Object} data A data block which is used by an Roo.data.Store object as
6473      * a cache of Roo.data.Records.
6474      */
6475     readRecords : function(o){
6476         var sid = this.meta ? this.meta.id : null;
6477         var recordType = this.recordType, fields = recordType.prototype.fields;
6478         var records = [];
6479         var root = o;
6480             for(var i = 0; i < root.length; i++){
6481                     var n = root[i];
6482                 var values = {};
6483                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6484                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6485                 var f = fields.items[j];
6486                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6487                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6488                 v = f.convert(v);
6489                 values[f.name] = v;
6490             }
6491                 var record = new recordType(values, id);
6492                 record.json = n;
6493                 records[records.length] = record;
6494             }
6495             return {
6496                 records : records,
6497                 totalRecords : records.length
6498             };
6499     }
6500 });/*
6501  * Based on:
6502  * Ext JS Library 1.1.1
6503  * Copyright(c) 2006-2007, Ext JS, LLC.
6504  *
6505  * Originally Released Under LGPL - original licence link has changed is not relivant.
6506  *
6507  * Fork - LGPL
6508  * <script type="text/javascript">
6509  */
6510
6511
6512 /**
6513  * @class Roo.data.Tree
6514  * @extends Roo.util.Observable
6515  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6516  * in the tree have most standard DOM functionality.
6517  * @constructor
6518  * @param {Node} root (optional) The root node
6519  */
6520 Roo.data.Tree = function(root){
6521    this.nodeHash = {};
6522    /**
6523     * The root node for this tree
6524     * @type Node
6525     */
6526    this.root = null;
6527    if(root){
6528        this.setRootNode(root);
6529    }
6530    this.addEvents({
6531        /**
6532         * @event append
6533         * Fires when a new child node is appended to a node in this tree.
6534         * @param {Tree} tree The owner tree
6535         * @param {Node} parent The parent node
6536         * @param {Node} node The newly appended node
6537         * @param {Number} index The index of the newly appended node
6538         */
6539        "append" : true,
6540        /**
6541         * @event remove
6542         * Fires when a child node is removed from a node in this tree.
6543         * @param {Tree} tree The owner tree
6544         * @param {Node} parent The parent node
6545         * @param {Node} node The child node removed
6546         */
6547        "remove" : true,
6548        /**
6549         * @event move
6550         * Fires when a node is moved to a new location in the tree
6551         * @param {Tree} tree The owner tree
6552         * @param {Node} node The node moved
6553         * @param {Node} oldParent The old parent of this node
6554         * @param {Node} newParent The new parent of this node
6555         * @param {Number} index The index it was moved to
6556         */
6557        "move" : true,
6558        /**
6559         * @event insert
6560         * Fires when a new child node is inserted in a node in this tree.
6561         * @param {Tree} tree The owner tree
6562         * @param {Node} parent The parent node
6563         * @param {Node} node The child node inserted
6564         * @param {Node} refNode The child node the node was inserted before
6565         */
6566        "insert" : true,
6567        /**
6568         * @event beforeappend
6569         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6570         * @param {Tree} tree The owner tree
6571         * @param {Node} parent The parent node
6572         * @param {Node} node The child node to be appended
6573         */
6574        "beforeappend" : true,
6575        /**
6576         * @event beforeremove
6577         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6578         * @param {Tree} tree The owner tree
6579         * @param {Node} parent The parent node
6580         * @param {Node} node The child node to be removed
6581         */
6582        "beforeremove" : true,
6583        /**
6584         * @event beforemove
6585         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6586         * @param {Tree} tree The owner tree
6587         * @param {Node} node The node being moved
6588         * @param {Node} oldParent The parent of the node
6589         * @param {Node} newParent The new parent the node is moving to
6590         * @param {Number} index The index it is being moved to
6591         */
6592        "beforemove" : true,
6593        /**
6594         * @event beforeinsert
6595         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6596         * @param {Tree} tree The owner tree
6597         * @param {Node} parent The parent node
6598         * @param {Node} node The child node to be inserted
6599         * @param {Node} refNode The child node the node is being inserted before
6600         */
6601        "beforeinsert" : true
6602    });
6603
6604     Roo.data.Tree.superclass.constructor.call(this);
6605 };
6606
6607 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6608     pathSeparator: "/",
6609
6610     proxyNodeEvent : function(){
6611         return this.fireEvent.apply(this, arguments);
6612     },
6613
6614     /**
6615      * Returns the root node for this tree.
6616      * @return {Node}
6617      */
6618     getRootNode : function(){
6619         return this.root;
6620     },
6621
6622     /**
6623      * Sets the root node for this tree.
6624      * @param {Node} node
6625      * @return {Node}
6626      */
6627     setRootNode : function(node){
6628         this.root = node;
6629         node.ownerTree = this;
6630         node.isRoot = true;
6631         this.registerNode(node);
6632         return node;
6633     },
6634
6635     /**
6636      * Gets a node in this tree by its id.
6637      * @param {String} id
6638      * @return {Node}
6639      */
6640     getNodeById : function(id){
6641         return this.nodeHash[id];
6642     },
6643
6644     registerNode : function(node){
6645         this.nodeHash[node.id] = node;
6646     },
6647
6648     unregisterNode : function(node){
6649         delete this.nodeHash[node.id];
6650     },
6651
6652     toString : function(){
6653         return "[Tree"+(this.id?" "+this.id:"")+"]";
6654     }
6655 });
6656
6657 /**
6658  * @class Roo.data.Node
6659  * @extends Roo.util.Observable
6660  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6661  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6662  * @constructor
6663  * @param {Object} attributes The attributes/config for the node
6664  */
6665 Roo.data.Node = function(attributes){
6666     /**
6667      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6668      * @type {Object}
6669      */
6670     this.attributes = attributes || {};
6671     this.leaf = this.attributes.leaf;
6672     /**
6673      * The node id. @type String
6674      */
6675     this.id = this.attributes.id;
6676     if(!this.id){
6677         this.id = Roo.id(null, "ynode-");
6678         this.attributes.id = this.id;
6679     }
6680      
6681     
6682     /**
6683      * All child nodes of this node. @type Array
6684      */
6685     this.childNodes = [];
6686     if(!this.childNodes.indexOf){ // indexOf is a must
6687         this.childNodes.indexOf = function(o){
6688             for(var i = 0, len = this.length; i < len; i++){
6689                 if(this[i] == o) {
6690                     return i;
6691                 }
6692             }
6693             return -1;
6694         };
6695     }
6696     /**
6697      * The parent node for this node. @type Node
6698      */
6699     this.parentNode = null;
6700     /**
6701      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6702      */
6703     this.firstChild = null;
6704     /**
6705      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6706      */
6707     this.lastChild = null;
6708     /**
6709      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6710      */
6711     this.previousSibling = null;
6712     /**
6713      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6714      */
6715     this.nextSibling = null;
6716
6717     this.addEvents({
6718        /**
6719         * @event append
6720         * Fires when a new child node is appended
6721         * @param {Tree} tree The owner tree
6722         * @param {Node} this This node
6723         * @param {Node} node The newly appended node
6724         * @param {Number} index The index of the newly appended node
6725         */
6726        "append" : true,
6727        /**
6728         * @event remove
6729         * Fires when a child node is removed
6730         * @param {Tree} tree The owner tree
6731         * @param {Node} this This node
6732         * @param {Node} node The removed node
6733         */
6734        "remove" : true,
6735        /**
6736         * @event move
6737         * Fires when this node is moved to a new location in the tree
6738         * @param {Tree} tree The owner tree
6739         * @param {Node} this This node
6740         * @param {Node} oldParent The old parent of this node
6741         * @param {Node} newParent The new parent of this node
6742         * @param {Number} index The index it was moved to
6743         */
6744        "move" : true,
6745        /**
6746         * @event insert
6747         * Fires when a new child node is inserted.
6748         * @param {Tree} tree The owner tree
6749         * @param {Node} this This node
6750         * @param {Node} node The child node inserted
6751         * @param {Node} refNode The child node the node was inserted before
6752         */
6753        "insert" : true,
6754        /**
6755         * @event beforeappend
6756         * Fires before a new child is appended, return false to cancel the append.
6757         * @param {Tree} tree The owner tree
6758         * @param {Node} this This node
6759         * @param {Node} node The child node to be appended
6760         */
6761        "beforeappend" : true,
6762        /**
6763         * @event beforeremove
6764         * Fires before a child is removed, return false to cancel the remove.
6765         * @param {Tree} tree The owner tree
6766         * @param {Node} this This node
6767         * @param {Node} node The child node to be removed
6768         */
6769        "beforeremove" : true,
6770        /**
6771         * @event beforemove
6772         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6773         * @param {Tree} tree The owner tree
6774         * @param {Node} this This node
6775         * @param {Node} oldParent The parent of this node
6776         * @param {Node} newParent The new parent this node is moving to
6777         * @param {Number} index The index it is being moved to
6778         */
6779        "beforemove" : true,
6780        /**
6781         * @event beforeinsert
6782         * Fires before a new child is inserted, return false to cancel the insert.
6783         * @param {Tree} tree The owner tree
6784         * @param {Node} this This node
6785         * @param {Node} node The child node to be inserted
6786         * @param {Node} refNode The child node the node is being inserted before
6787         */
6788        "beforeinsert" : true
6789    });
6790     this.listeners = this.attributes.listeners;
6791     Roo.data.Node.superclass.constructor.call(this);
6792 };
6793
6794 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6795     fireEvent : function(evtName){
6796         // first do standard event for this node
6797         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6798             return false;
6799         }
6800         // then bubble it up to the tree if the event wasn't cancelled
6801         var ot = this.getOwnerTree();
6802         if(ot){
6803             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6804                 return false;
6805             }
6806         }
6807         return true;
6808     },
6809
6810     /**
6811      * Returns true if this node is a leaf
6812      * @return {Boolean}
6813      */
6814     isLeaf : function(){
6815         return this.leaf === true;
6816     },
6817
6818     // private
6819     setFirstChild : function(node){
6820         this.firstChild = node;
6821     },
6822
6823     //private
6824     setLastChild : function(node){
6825         this.lastChild = node;
6826     },
6827
6828
6829     /**
6830      * Returns true if this node is the last child of its parent
6831      * @return {Boolean}
6832      */
6833     isLast : function(){
6834        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6835     },
6836
6837     /**
6838      * Returns true if this node is the first child of its parent
6839      * @return {Boolean}
6840      */
6841     isFirst : function(){
6842        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6843     },
6844
6845     hasChildNodes : function(){
6846         return !this.isLeaf() && this.childNodes.length > 0;
6847     },
6848
6849     /**
6850      * Insert node(s) as the last child node of this node.
6851      * @param {Node/Array} node The node or Array of nodes to append
6852      * @return {Node} The appended node if single append, or null if an array was passed
6853      */
6854     appendChild : function(node){
6855         var multi = false;
6856         if(node instanceof Array){
6857             multi = node;
6858         }else if(arguments.length > 1){
6859             multi = arguments;
6860         }
6861         // if passed an array or multiple args do them one by one
6862         if(multi){
6863             for(var i = 0, len = multi.length; i < len; i++) {
6864                 this.appendChild(multi[i]);
6865             }
6866         }else{
6867             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6868                 return false;
6869             }
6870             var index = this.childNodes.length;
6871             var oldParent = node.parentNode;
6872             // it's a move, make sure we move it cleanly
6873             if(oldParent){
6874                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6875                     return false;
6876                 }
6877                 oldParent.removeChild(node);
6878             }
6879             index = this.childNodes.length;
6880             if(index == 0){
6881                 this.setFirstChild(node);
6882             }
6883             this.childNodes.push(node);
6884             node.parentNode = this;
6885             var ps = this.childNodes[index-1];
6886             if(ps){
6887                 node.previousSibling = ps;
6888                 ps.nextSibling = node;
6889             }else{
6890                 node.previousSibling = null;
6891             }
6892             node.nextSibling = null;
6893             this.setLastChild(node);
6894             node.setOwnerTree(this.getOwnerTree());
6895             this.fireEvent("append", this.ownerTree, this, node, index);
6896             if(oldParent){
6897                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6898             }
6899             return node;
6900         }
6901     },
6902
6903     /**
6904      * Removes a child node from this node.
6905      * @param {Node} node The node to remove
6906      * @return {Node} The removed node
6907      */
6908     removeChild : function(node){
6909         var index = this.childNodes.indexOf(node);
6910         if(index == -1){
6911             return false;
6912         }
6913         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6914             return false;
6915         }
6916
6917         // remove it from childNodes collection
6918         this.childNodes.splice(index, 1);
6919
6920         // update siblings
6921         if(node.previousSibling){
6922             node.previousSibling.nextSibling = node.nextSibling;
6923         }
6924         if(node.nextSibling){
6925             node.nextSibling.previousSibling = node.previousSibling;
6926         }
6927
6928         // update child refs
6929         if(this.firstChild == node){
6930             this.setFirstChild(node.nextSibling);
6931         }
6932         if(this.lastChild == node){
6933             this.setLastChild(node.previousSibling);
6934         }
6935
6936         node.setOwnerTree(null);
6937         // clear any references from the node
6938         node.parentNode = null;
6939         node.previousSibling = null;
6940         node.nextSibling = null;
6941         this.fireEvent("remove", this.ownerTree, this, node);
6942         return node;
6943     },
6944
6945     /**
6946      * Inserts the first node before the second node in this nodes childNodes collection.
6947      * @param {Node} node The node to insert
6948      * @param {Node} refNode The node to insert before (if null the node is appended)
6949      * @return {Node} The inserted node
6950      */
6951     insertBefore : function(node, refNode){
6952         if(!refNode){ // like standard Dom, refNode can be null for append
6953             return this.appendChild(node);
6954         }
6955         // nothing to do
6956         if(node == refNode){
6957             return false;
6958         }
6959
6960         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6961             return false;
6962         }
6963         var index = this.childNodes.indexOf(refNode);
6964         var oldParent = node.parentNode;
6965         var refIndex = index;
6966
6967         // when moving internally, indexes will change after remove
6968         if(oldParent == this && this.childNodes.indexOf(node) < index){
6969             refIndex--;
6970         }
6971
6972         // it's a move, make sure we move it cleanly
6973         if(oldParent){
6974             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6975                 return false;
6976             }
6977             oldParent.removeChild(node);
6978         }
6979         if(refIndex == 0){
6980             this.setFirstChild(node);
6981         }
6982         this.childNodes.splice(refIndex, 0, node);
6983         node.parentNode = this;
6984         var ps = this.childNodes[refIndex-1];
6985         if(ps){
6986             node.previousSibling = ps;
6987             ps.nextSibling = node;
6988         }else{
6989             node.previousSibling = null;
6990         }
6991         node.nextSibling = refNode;
6992         refNode.previousSibling = node;
6993         node.setOwnerTree(this.getOwnerTree());
6994         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6995         if(oldParent){
6996             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6997         }
6998         return node;
6999     },
7000
7001     /**
7002      * Returns the child node at the specified index.
7003      * @param {Number} index
7004      * @return {Node}
7005      */
7006     item : function(index){
7007         return this.childNodes[index];
7008     },
7009
7010     /**
7011      * Replaces one child node in this node with another.
7012      * @param {Node} newChild The replacement node
7013      * @param {Node} oldChild The node to replace
7014      * @return {Node} The replaced node
7015      */
7016     replaceChild : function(newChild, oldChild){
7017         this.insertBefore(newChild, oldChild);
7018         this.removeChild(oldChild);
7019         return oldChild;
7020     },
7021
7022     /**
7023      * Returns the index of a child node
7024      * @param {Node} node
7025      * @return {Number} The index of the node or -1 if it was not found
7026      */
7027     indexOf : function(child){
7028         return this.childNodes.indexOf(child);
7029     },
7030
7031     /**
7032      * Returns the tree this node is in.
7033      * @return {Tree}
7034      */
7035     getOwnerTree : function(){
7036         // if it doesn't have one, look for one
7037         if(!this.ownerTree){
7038             var p = this;
7039             while(p){
7040                 if(p.ownerTree){
7041                     this.ownerTree = p.ownerTree;
7042                     break;
7043                 }
7044                 p = p.parentNode;
7045             }
7046         }
7047         return this.ownerTree;
7048     },
7049
7050     /**
7051      * Returns depth of this node (the root node has a depth of 0)
7052      * @return {Number}
7053      */
7054     getDepth : function(){
7055         var depth = 0;
7056         var p = this;
7057         while(p.parentNode){
7058             ++depth;
7059             p = p.parentNode;
7060         }
7061         return depth;
7062     },
7063
7064     // private
7065     setOwnerTree : function(tree){
7066         // if it's move, we need to update everyone
7067         if(tree != this.ownerTree){
7068             if(this.ownerTree){
7069                 this.ownerTree.unregisterNode(this);
7070             }
7071             this.ownerTree = tree;
7072             var cs = this.childNodes;
7073             for(var i = 0, len = cs.length; i < len; i++) {
7074                 cs[i].setOwnerTree(tree);
7075             }
7076             if(tree){
7077                 tree.registerNode(this);
7078             }
7079         }
7080     },
7081
7082     /**
7083      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7084      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7085      * @return {String} The path
7086      */
7087     getPath : function(attr){
7088         attr = attr || "id";
7089         var p = this.parentNode;
7090         var b = [this.attributes[attr]];
7091         while(p){
7092             b.unshift(p.attributes[attr]);
7093             p = p.parentNode;
7094         }
7095         var sep = this.getOwnerTree().pathSeparator;
7096         return sep + b.join(sep);
7097     },
7098
7099     /**
7100      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7101      * function call will be the scope provided or the current node. The arguments to the function
7102      * will be the args provided or the current node. If the function returns false at any point,
7103      * the bubble is stopped.
7104      * @param {Function} fn The function to call
7105      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7106      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7107      */
7108     bubble : function(fn, scope, args){
7109         var p = this;
7110         while(p){
7111             if(fn.call(scope || p, args || p) === false){
7112                 break;
7113             }
7114             p = p.parentNode;
7115         }
7116     },
7117
7118     /**
7119      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7120      * function call will be the scope provided or the current node. The arguments to the function
7121      * will be the args provided or the current node. If the function returns false at any point,
7122      * the cascade is stopped on that branch.
7123      * @param {Function} fn The function to call
7124      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7125      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7126      */
7127     cascade : function(fn, scope, args){
7128         if(fn.call(scope || this, args || this) !== false){
7129             var cs = this.childNodes;
7130             for(var i = 0, len = cs.length; i < len; i++) {
7131                 cs[i].cascade(fn, scope, args);
7132             }
7133         }
7134     },
7135
7136     /**
7137      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7138      * function call will be the scope provided or the current node. The arguments to the function
7139      * will be the args provided or the current node. If the function returns false at any point,
7140      * the iteration stops.
7141      * @param {Function} fn The function to call
7142      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7143      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7144      */
7145     eachChild : function(fn, scope, args){
7146         var cs = this.childNodes;
7147         for(var i = 0, len = cs.length; i < len; i++) {
7148                 if(fn.call(scope || this, args || cs[i]) === false){
7149                     break;
7150                 }
7151         }
7152     },
7153
7154     /**
7155      * Finds the first child that has the attribute with the specified value.
7156      * @param {String} attribute The attribute name
7157      * @param {Mixed} value The value to search for
7158      * @return {Node} The found child or null if none was found
7159      */
7160     findChild : function(attribute, value){
7161         var cs = this.childNodes;
7162         for(var i = 0, len = cs.length; i < len; i++) {
7163                 if(cs[i].attributes[attribute] == value){
7164                     return cs[i];
7165                 }
7166         }
7167         return null;
7168     },
7169
7170     /**
7171      * Finds the first child by a custom function. The child matches if the function passed
7172      * returns true.
7173      * @param {Function} fn
7174      * @param {Object} scope (optional)
7175      * @return {Node} The found child or null if none was found
7176      */
7177     findChildBy : function(fn, scope){
7178         var cs = this.childNodes;
7179         for(var i = 0, len = cs.length; i < len; i++) {
7180                 if(fn.call(scope||cs[i], cs[i]) === true){
7181                     return cs[i];
7182                 }
7183         }
7184         return null;
7185     },
7186
7187     /**
7188      * Sorts this nodes children using the supplied sort function
7189      * @param {Function} fn
7190      * @param {Object} scope (optional)
7191      */
7192     sort : function(fn, scope){
7193         var cs = this.childNodes;
7194         var len = cs.length;
7195         if(len > 0){
7196             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7197             cs.sort(sortFn);
7198             for(var i = 0; i < len; i++){
7199                 var n = cs[i];
7200                 n.previousSibling = cs[i-1];
7201                 n.nextSibling = cs[i+1];
7202                 if(i == 0){
7203                     this.setFirstChild(n);
7204                 }
7205                 if(i == len-1){
7206                     this.setLastChild(n);
7207                 }
7208             }
7209         }
7210     },
7211
7212     /**
7213      * Returns true if this node is an ancestor (at any point) of the passed node.
7214      * @param {Node} node
7215      * @return {Boolean}
7216      */
7217     contains : function(node){
7218         return node.isAncestor(this);
7219     },
7220
7221     /**
7222      * Returns true if the passed node is an ancestor (at any point) of this node.
7223      * @param {Node} node
7224      * @return {Boolean}
7225      */
7226     isAncestor : function(node){
7227         var p = this.parentNode;
7228         while(p){
7229             if(p == node){
7230                 return true;
7231             }
7232             p = p.parentNode;
7233         }
7234         return false;
7235     },
7236
7237     toString : function(){
7238         return "[Node"+(this.id?" "+this.id:"")+"]";
7239     }
7240 });/*
7241  * Based on:
7242  * Ext JS Library 1.1.1
7243  * Copyright(c) 2006-2007, Ext JS, LLC.
7244  *
7245  * Originally Released Under LGPL - original licence link has changed is not relivant.
7246  *
7247  * Fork - LGPL
7248  * <script type="text/javascript">
7249  */
7250  
7251
7252 /**
7253  * @class Roo.ComponentMgr
7254  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7255  * @singleton
7256  */
7257 Roo.ComponentMgr = function(){
7258     var all = new Roo.util.MixedCollection();
7259
7260     return {
7261         /**
7262          * Registers a component.
7263          * @param {Roo.Component} c The component
7264          */
7265         register : function(c){
7266             all.add(c);
7267         },
7268
7269         /**
7270          * Unregisters a component.
7271          * @param {Roo.Component} c The component
7272          */
7273         unregister : function(c){
7274             all.remove(c);
7275         },
7276
7277         /**
7278          * Returns a component by id
7279          * @param {String} id The component id
7280          */
7281         get : function(id){
7282             return all.get(id);
7283         },
7284
7285         /**
7286          * Registers a function that will be called when a specified component is added to ComponentMgr
7287          * @param {String} id The component id
7288          * @param {Funtction} fn The callback function
7289          * @param {Object} scope The scope of the callback
7290          */
7291         onAvailable : function(id, fn, scope){
7292             all.on("add", function(index, o){
7293                 if(o.id == id){
7294                     fn.call(scope || o, o);
7295                     all.un("add", fn, scope);
7296                 }
7297             });
7298         }
7299     };
7300 }();/*
7301  * Based on:
7302  * Ext JS Library 1.1.1
7303  * Copyright(c) 2006-2007, Ext JS, LLC.
7304  *
7305  * Originally Released Under LGPL - original licence link has changed is not relivant.
7306  *
7307  * Fork - LGPL
7308  * <script type="text/javascript">
7309  */
7310  
7311 /**
7312  * @class Roo.Component
7313  * @extends Roo.util.Observable
7314  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7315  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7316  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7317  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7318  * All visual components (widgets) that require rendering into a layout should subclass Component.
7319  * @constructor
7320  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7321  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
7322  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7323  */
7324 Roo.Component = function(config){
7325     config = config || {};
7326     if(config.tagName || config.dom || typeof config == "string"){ // element object
7327         config = {el: config, id: config.id || config};
7328     }
7329     this.initialConfig = config;
7330
7331     Roo.apply(this, config);
7332     this.addEvents({
7333         /**
7334          * @event disable
7335          * Fires after the component is disabled.
7336              * @param {Roo.Component} this
7337              */
7338         disable : true,
7339         /**
7340          * @event enable
7341          * Fires after the component is enabled.
7342              * @param {Roo.Component} this
7343              */
7344         enable : true,
7345         /**
7346          * @event beforeshow
7347          * Fires before the component is shown.  Return false to stop the show.
7348              * @param {Roo.Component} this
7349              */
7350         beforeshow : true,
7351         /**
7352          * @event show
7353          * Fires after the component is shown.
7354              * @param {Roo.Component} this
7355              */
7356         show : true,
7357         /**
7358          * @event beforehide
7359          * Fires before the component is hidden. Return false to stop the hide.
7360              * @param {Roo.Component} this
7361              */
7362         beforehide : true,
7363         /**
7364          * @event hide
7365          * Fires after the component is hidden.
7366              * @param {Roo.Component} this
7367              */
7368         hide : true,
7369         /**
7370          * @event beforerender
7371          * Fires before the component is rendered. Return false to stop the render.
7372              * @param {Roo.Component} this
7373              */
7374         beforerender : true,
7375         /**
7376          * @event render
7377          * Fires after the component is rendered.
7378              * @param {Roo.Component} this
7379              */
7380         render : true,
7381         /**
7382          * @event beforedestroy
7383          * Fires before the component is destroyed. Return false to stop the destroy.
7384              * @param {Roo.Component} this
7385              */
7386         beforedestroy : true,
7387         /**
7388          * @event destroy
7389          * Fires after the component is destroyed.
7390              * @param {Roo.Component} this
7391              */
7392         destroy : true
7393     });
7394     if(!this.id){
7395         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7396     }
7397     Roo.ComponentMgr.register(this);
7398     Roo.Component.superclass.constructor.call(this);
7399     this.initComponent();
7400     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7401         this.render(this.renderTo);
7402         delete this.renderTo;
7403     }
7404 };
7405
7406 /** @private */
7407 Roo.Component.AUTO_ID = 1000;
7408
7409 Roo.extend(Roo.Component, Roo.util.Observable, {
7410     /**
7411      * @scope Roo.Component.prototype
7412      * @type {Boolean}
7413      * true if this component is hidden. Read-only.
7414      */
7415     hidden : false,
7416     /**
7417      * @type {Boolean}
7418      * true if this component is disabled. Read-only.
7419      */
7420     disabled : false,
7421     /**
7422      * @type {Boolean}
7423      * true if this component has been rendered. Read-only.
7424      */
7425     rendered : false,
7426     
7427     /** @cfg {String} disableClass
7428      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7429      */
7430     disabledClass : "x-item-disabled",
7431         /** @cfg {Boolean} allowDomMove
7432          * Whether the component can move the Dom node when rendering (defaults to true).
7433          */
7434     allowDomMove : true,
7435     /** @cfg {String} hideMode
7436      * How this component should hidden. Supported values are
7437      * "visibility" (css visibility), "offsets" (negative offset position) and
7438      * "display" (css display) - defaults to "display".
7439      */
7440     hideMode: 'display',
7441
7442     /** @private */
7443     ctype : "Roo.Component",
7444
7445     /**
7446      * @cfg {String} actionMode 
7447      * which property holds the element that used for  hide() / show() / disable() / enable()
7448      * default is 'el' 
7449      */
7450     actionMode : "el",
7451
7452     /** @private */
7453     getActionEl : function(){
7454         return this[this.actionMode];
7455     },
7456
7457     initComponent : Roo.emptyFn,
7458     /**
7459      * If this is a lazy rendering component, render it to its container element.
7460      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
7461      */
7462     render : function(container, position){
7463         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7464             if(!container && this.el){
7465                 this.el = Roo.get(this.el);
7466                 container = this.el.dom.parentNode;
7467                 this.allowDomMove = false;
7468             }
7469             this.container = Roo.get(container);
7470             this.rendered = true;
7471             if(position !== undefined){
7472                 if(typeof position == 'number'){
7473                     position = this.container.dom.childNodes[position];
7474                 }else{
7475                     position = Roo.getDom(position);
7476                 }
7477             }
7478             this.onRender(this.container, position || null);
7479             if(this.cls){
7480                 this.el.addClass(this.cls);
7481                 delete this.cls;
7482             }
7483             if(this.style){
7484                 this.el.applyStyles(this.style);
7485                 delete this.style;
7486             }
7487             this.fireEvent("render", this);
7488             this.afterRender(this.container);
7489             if(this.hidden){
7490                 this.hide();
7491             }
7492             if(this.disabled){
7493                 this.disable();
7494             }
7495         }
7496         return this;
7497     },
7498
7499     /** @private */
7500     // default function is not really useful
7501     onRender : function(ct, position){
7502         if(this.el){
7503             this.el = Roo.get(this.el);
7504             if(this.allowDomMove !== false){
7505                 ct.dom.insertBefore(this.el.dom, position);
7506             }
7507         }
7508     },
7509
7510     /** @private */
7511     getAutoCreate : function(){
7512         var cfg = typeof this.autoCreate == "object" ?
7513                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7514         if(this.id && !cfg.id){
7515             cfg.id = this.id;
7516         }
7517         return cfg;
7518     },
7519
7520     /** @private */
7521     afterRender : Roo.emptyFn,
7522
7523     /**
7524      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7525      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7526      */
7527     destroy : function(){
7528         if(this.fireEvent("beforedestroy", this) !== false){
7529             this.purgeListeners();
7530             this.beforeDestroy();
7531             if(this.rendered){
7532                 this.el.removeAllListeners();
7533                 this.el.remove();
7534                 if(this.actionMode == "container"){
7535                     this.container.remove();
7536                 }
7537             }
7538             this.onDestroy();
7539             Roo.ComponentMgr.unregister(this);
7540             this.fireEvent("destroy", this);
7541         }
7542     },
7543
7544         /** @private */
7545     beforeDestroy : function(){
7546
7547     },
7548
7549         /** @private */
7550         onDestroy : function(){
7551
7552     },
7553
7554     /**
7555      * Returns the underlying {@link Roo.Element}.
7556      * @return {Roo.Element} The element
7557      */
7558     getEl : function(){
7559         return this.el;
7560     },
7561
7562     /**
7563      * Returns the id of this component.
7564      * @return {String}
7565      */
7566     getId : function(){
7567         return this.id;
7568     },
7569
7570     /**
7571      * Try to focus this component.
7572      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7573      * @return {Roo.Component} this
7574      */
7575     focus : function(selectText){
7576         if(this.rendered){
7577             this.el.focus();
7578             if(selectText === true){
7579                 this.el.dom.select();
7580             }
7581         }
7582         return this;
7583     },
7584
7585     /** @private */
7586     blur : function(){
7587         if(this.rendered){
7588             this.el.blur();
7589         }
7590         return this;
7591     },
7592
7593     /**
7594      * Disable this component.
7595      * @return {Roo.Component} this
7596      */
7597     disable : function(){
7598         if(this.rendered){
7599             this.onDisable();
7600         }
7601         this.disabled = true;
7602         this.fireEvent("disable", this);
7603         return this;
7604     },
7605
7606         // private
7607     onDisable : function(){
7608         this.getActionEl().addClass(this.disabledClass);
7609         this.el.dom.disabled = true;
7610     },
7611
7612     /**
7613      * Enable this component.
7614      * @return {Roo.Component} this
7615      */
7616     enable : function(){
7617         if(this.rendered){
7618             this.onEnable();
7619         }
7620         this.disabled = false;
7621         this.fireEvent("enable", this);
7622         return this;
7623     },
7624
7625         // private
7626     onEnable : function(){
7627         this.getActionEl().removeClass(this.disabledClass);
7628         this.el.dom.disabled = false;
7629     },
7630
7631     /**
7632      * Convenience function for setting disabled/enabled by boolean.
7633      * @param {Boolean} disabled
7634      */
7635     setDisabled : function(disabled){
7636         this[disabled ? "disable" : "enable"]();
7637     },
7638
7639     /**
7640      * Show this component.
7641      * @return {Roo.Component} this
7642      */
7643     show: function(){
7644         if(this.fireEvent("beforeshow", this) !== false){
7645             this.hidden = false;
7646             if(this.rendered){
7647                 this.onShow();
7648             }
7649             this.fireEvent("show", this);
7650         }
7651         return this;
7652     },
7653
7654     // private
7655     onShow : function(){
7656         var ae = this.getActionEl();
7657         if(this.hideMode == 'visibility'){
7658             ae.dom.style.visibility = "visible";
7659         }else if(this.hideMode == 'offsets'){
7660             ae.removeClass('x-hidden');
7661         }else{
7662             ae.dom.style.display = "";
7663         }
7664     },
7665
7666     /**
7667      * Hide this component.
7668      * @return {Roo.Component} this
7669      */
7670     hide: function(){
7671         if(this.fireEvent("beforehide", this) !== false){
7672             this.hidden = true;
7673             if(this.rendered){
7674                 this.onHide();
7675             }
7676             this.fireEvent("hide", this);
7677         }
7678         return this;
7679     },
7680
7681     // private
7682     onHide : function(){
7683         var ae = this.getActionEl();
7684         if(this.hideMode == 'visibility'){
7685             ae.dom.style.visibility = "hidden";
7686         }else if(this.hideMode == 'offsets'){
7687             ae.addClass('x-hidden');
7688         }else{
7689             ae.dom.style.display = "none";
7690         }
7691     },
7692
7693     /**
7694      * Convenience function to hide or show this component by boolean.
7695      * @param {Boolean} visible True to show, false to hide
7696      * @return {Roo.Component} this
7697      */
7698     setVisible: function(visible){
7699         if(visible) {
7700             this.show();
7701         }else{
7702             this.hide();
7703         }
7704         return this;
7705     },
7706
7707     /**
7708      * Returns true if this component is visible.
7709      */
7710     isVisible : function(){
7711         return this.getActionEl().isVisible();
7712     },
7713
7714     cloneConfig : function(overrides){
7715         overrides = overrides || {};
7716         var id = overrides.id || Roo.id();
7717         var cfg = Roo.applyIf(overrides, this.initialConfig);
7718         cfg.id = id; // prevent dup id
7719         return new this.constructor(cfg);
7720     }
7721 });/*
7722  * Based on:
7723  * Ext JS Library 1.1.1
7724  * Copyright(c) 2006-2007, Ext JS, LLC.
7725  *
7726  * Originally Released Under LGPL - original licence link has changed is not relivant.
7727  *
7728  * Fork - LGPL
7729  * <script type="text/javascript">
7730  */
7731  (function(){ 
7732 /**
7733  * @class Roo.Layer
7734  * @extends Roo.Element
7735  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7736  * automatic maintaining of shadow/shim positions.
7737  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7738  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7739  * you can pass a string with a CSS class name. False turns off the shadow.
7740  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7741  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7742  * @cfg {String} cls CSS class to add to the element
7743  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7744  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7745  * @constructor
7746  * @param {Object} config An object with config options.
7747  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7748  */
7749
7750 Roo.Layer = function(config, existingEl){
7751     config = config || {};
7752     var dh = Roo.DomHelper;
7753     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7754     if(existingEl){
7755         this.dom = Roo.getDom(existingEl);
7756     }
7757     if(!this.dom){
7758         var o = config.dh || {tag: "div", cls: "x-layer"};
7759         this.dom = dh.append(pel, o);
7760     }
7761     if(config.cls){
7762         this.addClass(config.cls);
7763     }
7764     this.constrain = config.constrain !== false;
7765     this.visibilityMode = Roo.Element.VISIBILITY;
7766     if(config.id){
7767         this.id = this.dom.id = config.id;
7768     }else{
7769         this.id = Roo.id(this.dom);
7770     }
7771     this.zindex = config.zindex || this.getZIndex();
7772     this.position("absolute", this.zindex);
7773     if(config.shadow){
7774         this.shadowOffset = config.shadowOffset || 4;
7775         this.shadow = new Roo.Shadow({
7776             offset : this.shadowOffset,
7777             mode : config.shadow
7778         });
7779     }else{
7780         this.shadowOffset = 0;
7781     }
7782     this.useShim = config.shim !== false && Roo.useShims;
7783     this.useDisplay = config.useDisplay;
7784     this.hide();
7785 };
7786
7787 var supr = Roo.Element.prototype;
7788
7789 // shims are shared among layer to keep from having 100 iframes
7790 var shims = [];
7791
7792 Roo.extend(Roo.Layer, Roo.Element, {
7793
7794     getZIndex : function(){
7795         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7796     },
7797
7798     getShim : function(){
7799         if(!this.useShim){
7800             return null;
7801         }
7802         if(this.shim){
7803             return this.shim;
7804         }
7805         var shim = shims.shift();
7806         if(!shim){
7807             shim = this.createShim();
7808             shim.enableDisplayMode('block');
7809             shim.dom.style.display = 'none';
7810             shim.dom.style.visibility = 'visible';
7811         }
7812         var pn = this.dom.parentNode;
7813         if(shim.dom.parentNode != pn){
7814             pn.insertBefore(shim.dom, this.dom);
7815         }
7816         shim.setStyle('z-index', this.getZIndex()-2);
7817         this.shim = shim;
7818         return shim;
7819     },
7820
7821     hideShim : function(){
7822         if(this.shim){
7823             this.shim.setDisplayed(false);
7824             shims.push(this.shim);
7825             delete this.shim;
7826         }
7827     },
7828
7829     disableShadow : function(){
7830         if(this.shadow){
7831             this.shadowDisabled = true;
7832             this.shadow.hide();
7833             this.lastShadowOffset = this.shadowOffset;
7834             this.shadowOffset = 0;
7835         }
7836     },
7837
7838     enableShadow : function(show){
7839         if(this.shadow){
7840             this.shadowDisabled = false;
7841             this.shadowOffset = this.lastShadowOffset;
7842             delete this.lastShadowOffset;
7843             if(show){
7844                 this.sync(true);
7845             }
7846         }
7847     },
7848
7849     // private
7850     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7851     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7852     sync : function(doShow){
7853         var sw = this.shadow;
7854         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7855             var sh = this.getShim();
7856
7857             var w = this.getWidth(),
7858                 h = this.getHeight();
7859
7860             var l = this.getLeft(true),
7861                 t = this.getTop(true);
7862
7863             if(sw && !this.shadowDisabled){
7864                 if(doShow && !sw.isVisible()){
7865                     sw.show(this);
7866                 }else{
7867                     sw.realign(l, t, w, h);
7868                 }
7869                 if(sh){
7870                     if(doShow){
7871                        sh.show();
7872                     }
7873                     // fit the shim behind the shadow, so it is shimmed too
7874                     var a = sw.adjusts, s = sh.dom.style;
7875                     s.left = (Math.min(l, l+a.l))+"px";
7876                     s.top = (Math.min(t, t+a.t))+"px";
7877                     s.width = (w+a.w)+"px";
7878                     s.height = (h+a.h)+"px";
7879                 }
7880             }else if(sh){
7881                 if(doShow){
7882                    sh.show();
7883                 }
7884                 sh.setSize(w, h);
7885                 sh.setLeftTop(l, t);
7886             }
7887             
7888         }
7889     },
7890
7891     // private
7892     destroy : function(){
7893         this.hideShim();
7894         if(this.shadow){
7895             this.shadow.hide();
7896         }
7897         this.removeAllListeners();
7898         var pn = this.dom.parentNode;
7899         if(pn){
7900             pn.removeChild(this.dom);
7901         }
7902         Roo.Element.uncache(this.id);
7903     },
7904
7905     remove : function(){
7906         this.destroy();
7907     },
7908
7909     // private
7910     beginUpdate : function(){
7911         this.updating = true;
7912     },
7913
7914     // private
7915     endUpdate : function(){
7916         this.updating = false;
7917         this.sync(true);
7918     },
7919
7920     // private
7921     hideUnders : function(negOffset){
7922         if(this.shadow){
7923             this.shadow.hide();
7924         }
7925         this.hideShim();
7926     },
7927
7928     // private
7929     constrainXY : function(){
7930         if(this.constrain){
7931             var vw = Roo.lib.Dom.getViewWidth(),
7932                 vh = Roo.lib.Dom.getViewHeight();
7933             var s = Roo.get(document).getScroll();
7934
7935             var xy = this.getXY();
7936             var x = xy[0], y = xy[1];   
7937             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7938             // only move it if it needs it
7939             var moved = false;
7940             // first validate right/bottom
7941             if((x + w) > vw+s.left){
7942                 x = vw - w - this.shadowOffset;
7943                 moved = true;
7944             }
7945             if((y + h) > vh+s.top){
7946                 y = vh - h - this.shadowOffset;
7947                 moved = true;
7948             }
7949             // then make sure top/left isn't negative
7950             if(x < s.left){
7951                 x = s.left;
7952                 moved = true;
7953             }
7954             if(y < s.top){
7955                 y = s.top;
7956                 moved = true;
7957             }
7958             if(moved){
7959                 if(this.avoidY){
7960                     var ay = this.avoidY;
7961                     if(y <= ay && (y+h) >= ay){
7962                         y = ay-h-5;   
7963                     }
7964                 }
7965                 xy = [x, y];
7966                 this.storeXY(xy);
7967                 supr.setXY.call(this, xy);
7968                 this.sync();
7969             }
7970         }
7971     },
7972
7973     isVisible : function(){
7974         return this.visible;    
7975     },
7976
7977     // private
7978     showAction : function(){
7979         this.visible = true; // track visibility to prevent getStyle calls
7980         if(this.useDisplay === true){
7981             this.setDisplayed("");
7982         }else if(this.lastXY){
7983             supr.setXY.call(this, this.lastXY);
7984         }else if(this.lastLT){
7985             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7986         }
7987     },
7988
7989     // private
7990     hideAction : function(){
7991         this.visible = false;
7992         if(this.useDisplay === true){
7993             this.setDisplayed(false);
7994         }else{
7995             this.setLeftTop(-10000,-10000);
7996         }
7997     },
7998
7999     // overridden Element method
8000     setVisible : function(v, a, d, c, e){
8001         if(v){
8002             this.showAction();
8003         }
8004         if(a && v){
8005             var cb = function(){
8006                 this.sync(true);
8007                 if(c){
8008                     c();
8009                 }
8010             }.createDelegate(this);
8011             supr.setVisible.call(this, true, true, d, cb, e);
8012         }else{
8013             if(!v){
8014                 this.hideUnders(true);
8015             }
8016             var cb = c;
8017             if(a){
8018                 cb = function(){
8019                     this.hideAction();
8020                     if(c){
8021                         c();
8022                     }
8023                 }.createDelegate(this);
8024             }
8025             supr.setVisible.call(this, v, a, d, cb, e);
8026             if(v){
8027                 this.sync(true);
8028             }else if(!a){
8029                 this.hideAction();
8030             }
8031         }
8032     },
8033
8034     storeXY : function(xy){
8035         delete this.lastLT;
8036         this.lastXY = xy;
8037     },
8038
8039     storeLeftTop : function(left, top){
8040         delete this.lastXY;
8041         this.lastLT = [left, top];
8042     },
8043
8044     // private
8045     beforeFx : function(){
8046         this.beforeAction();
8047         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8048     },
8049
8050     // private
8051     afterFx : function(){
8052         Roo.Layer.superclass.afterFx.apply(this, arguments);
8053         this.sync(this.isVisible());
8054     },
8055
8056     // private
8057     beforeAction : function(){
8058         if(!this.updating && this.shadow){
8059             this.shadow.hide();
8060         }
8061     },
8062
8063     // overridden Element method
8064     setLeft : function(left){
8065         this.storeLeftTop(left, this.getTop(true));
8066         supr.setLeft.apply(this, arguments);
8067         this.sync();
8068     },
8069
8070     setTop : function(top){
8071         this.storeLeftTop(this.getLeft(true), top);
8072         supr.setTop.apply(this, arguments);
8073         this.sync();
8074     },
8075
8076     setLeftTop : function(left, top){
8077         this.storeLeftTop(left, top);
8078         supr.setLeftTop.apply(this, arguments);
8079         this.sync();
8080     },
8081
8082     setXY : function(xy, a, d, c, e){
8083         this.fixDisplay();
8084         this.beforeAction();
8085         this.storeXY(xy);
8086         var cb = this.createCB(c);
8087         supr.setXY.call(this, xy, a, d, cb, e);
8088         if(!a){
8089             cb();
8090         }
8091     },
8092
8093     // private
8094     createCB : function(c){
8095         var el = this;
8096         return function(){
8097             el.constrainXY();
8098             el.sync(true);
8099             if(c){
8100                 c();
8101             }
8102         };
8103     },
8104
8105     // overridden Element method
8106     setX : function(x, a, d, c, e){
8107         this.setXY([x, this.getY()], a, d, c, e);
8108     },
8109
8110     // overridden Element method
8111     setY : function(y, a, d, c, e){
8112         this.setXY([this.getX(), y], a, d, c, e);
8113     },
8114
8115     // overridden Element method
8116     setSize : function(w, h, a, d, c, e){
8117         this.beforeAction();
8118         var cb = this.createCB(c);
8119         supr.setSize.call(this, w, h, a, d, cb, e);
8120         if(!a){
8121             cb();
8122         }
8123     },
8124
8125     // overridden Element method
8126     setWidth : function(w, a, d, c, e){
8127         this.beforeAction();
8128         var cb = this.createCB(c);
8129         supr.setWidth.call(this, w, a, d, cb, e);
8130         if(!a){
8131             cb();
8132         }
8133     },
8134
8135     // overridden Element method
8136     setHeight : function(h, a, d, c, e){
8137         this.beforeAction();
8138         var cb = this.createCB(c);
8139         supr.setHeight.call(this, h, a, d, cb, e);
8140         if(!a){
8141             cb();
8142         }
8143     },
8144
8145     // overridden Element method
8146     setBounds : function(x, y, w, h, a, d, c, e){
8147         this.beforeAction();
8148         var cb = this.createCB(c);
8149         if(!a){
8150             this.storeXY([x, y]);
8151             supr.setXY.call(this, [x, y]);
8152             supr.setSize.call(this, w, h, a, d, cb, e);
8153             cb();
8154         }else{
8155             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8156         }
8157         return this;
8158     },
8159     
8160     /**
8161      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8162      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8163      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8164      * @param {Number} zindex The new z-index to set
8165      * @return {this} The Layer
8166      */
8167     setZIndex : function(zindex){
8168         this.zindex = zindex;
8169         this.setStyle("z-index", zindex + 2);
8170         if(this.shadow){
8171             this.shadow.setZIndex(zindex + 1);
8172         }
8173         if(this.shim){
8174             this.shim.setStyle("z-index", zindex);
8175         }
8176     }
8177 });
8178 })();/*
8179  * Based on:
8180  * Ext JS Library 1.1.1
8181  * Copyright(c) 2006-2007, Ext JS, LLC.
8182  *
8183  * Originally Released Under LGPL - original licence link has changed is not relivant.
8184  *
8185  * Fork - LGPL
8186  * <script type="text/javascript">
8187  */
8188
8189
8190 /**
8191  * @class Roo.Shadow
8192  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8193  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8194  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8195  * @constructor
8196  * Create a new Shadow
8197  * @param {Object} config The config object
8198  */
8199 Roo.Shadow = function(config){
8200     Roo.apply(this, config);
8201     if(typeof this.mode != "string"){
8202         this.mode = this.defaultMode;
8203     }
8204     var o = this.offset, a = {h: 0};
8205     var rad = Math.floor(this.offset/2);
8206     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8207         case "drop":
8208             a.w = 0;
8209             a.l = a.t = o;
8210             a.t -= 1;
8211             if(Roo.isIE){
8212                 a.l -= this.offset + rad;
8213                 a.t -= this.offset + rad;
8214                 a.w -= rad;
8215                 a.h -= rad;
8216                 a.t += 1;
8217             }
8218         break;
8219         case "sides":
8220             a.w = (o*2);
8221             a.l = -o;
8222             a.t = o-1;
8223             if(Roo.isIE){
8224                 a.l -= (this.offset - rad);
8225                 a.t -= this.offset + rad;
8226                 a.l += 1;
8227                 a.w -= (this.offset - rad)*2;
8228                 a.w -= rad + 1;
8229                 a.h -= 1;
8230             }
8231         break;
8232         case "frame":
8233             a.w = a.h = (o*2);
8234             a.l = a.t = -o;
8235             a.t += 1;
8236             a.h -= 2;
8237             if(Roo.isIE){
8238                 a.l -= (this.offset - rad);
8239                 a.t -= (this.offset - rad);
8240                 a.l += 1;
8241                 a.w -= (this.offset + rad + 1);
8242                 a.h -= (this.offset + rad);
8243                 a.h += 1;
8244             }
8245         break;
8246     };
8247
8248     this.adjusts = a;
8249 };
8250
8251 Roo.Shadow.prototype = {
8252     /**
8253      * @cfg {String} mode
8254      * The shadow display mode.  Supports the following options:<br />
8255      * sides: Shadow displays on both sides and bottom only<br />
8256      * frame: Shadow displays equally on all four sides<br />
8257      * drop: Traditional bottom-right drop shadow (default)
8258      */
8259     /**
8260      * @cfg {String} offset
8261      * The number of pixels to offset the shadow from the element (defaults to 4)
8262      */
8263     offset: 4,
8264
8265     // private
8266     defaultMode: "drop",
8267
8268     /**
8269      * Displays the shadow under the target element
8270      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8271      */
8272     show : function(target){
8273         target = Roo.get(target);
8274         if(!this.el){
8275             this.el = Roo.Shadow.Pool.pull();
8276             if(this.el.dom.nextSibling != target.dom){
8277                 this.el.insertBefore(target);
8278             }
8279         }
8280         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8281         if(Roo.isIE){
8282             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8283         }
8284         this.realign(
8285             target.getLeft(true),
8286             target.getTop(true),
8287             target.getWidth(),
8288             target.getHeight()
8289         );
8290         this.el.dom.style.display = "block";
8291     },
8292
8293     /**
8294      * Returns true if the shadow is visible, else false
8295      */
8296     isVisible : function(){
8297         return this.el ? true : false;  
8298     },
8299
8300     /**
8301      * Direct alignment when values are already available. Show must be called at least once before
8302      * calling this method to ensure it is initialized.
8303      * @param {Number} left The target element left position
8304      * @param {Number} top The target element top position
8305      * @param {Number} width The target element width
8306      * @param {Number} height The target element height
8307      */
8308     realign : function(l, t, w, h){
8309         if(!this.el){
8310             return;
8311         }
8312         var a = this.adjusts, d = this.el.dom, s = d.style;
8313         var iea = 0;
8314         s.left = (l+a.l)+"px";
8315         s.top = (t+a.t)+"px";
8316         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8317  
8318         if(s.width != sws || s.height != shs){
8319             s.width = sws;
8320             s.height = shs;
8321             if(!Roo.isIE){
8322                 var cn = d.childNodes;
8323                 var sww = Math.max(0, (sw-12))+"px";
8324                 cn[0].childNodes[1].style.width = sww;
8325                 cn[1].childNodes[1].style.width = sww;
8326                 cn[2].childNodes[1].style.width = sww;
8327                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8328             }
8329         }
8330     },
8331
8332     /**
8333      * Hides this shadow
8334      */
8335     hide : function(){
8336         if(this.el){
8337             this.el.dom.style.display = "none";
8338             Roo.Shadow.Pool.push(this.el);
8339             delete this.el;
8340         }
8341     },
8342
8343     /**
8344      * Adjust the z-index of this shadow
8345      * @param {Number} zindex The new z-index
8346      */
8347     setZIndex : function(z){
8348         this.zIndex = z;
8349         if(this.el){
8350             this.el.setStyle("z-index", z);
8351         }
8352     }
8353 };
8354
8355 // Private utility class that manages the internal Shadow cache
8356 Roo.Shadow.Pool = function(){
8357     var p = [];
8358     var markup = Roo.isIE ?
8359                  '<div class="x-ie-shadow"></div>' :
8360                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
8361     return {
8362         pull : function(){
8363             var sh = p.shift();
8364             if(!sh){
8365                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8366                 sh.autoBoxAdjust = false;
8367             }
8368             return sh;
8369         },
8370
8371         push : function(sh){
8372             p.push(sh);
8373         }
8374     };
8375 }();/*
8376  * Based on:
8377  * Ext JS Library 1.1.1
8378  * Copyright(c) 2006-2007, Ext JS, LLC.
8379  *
8380  * Originally Released Under LGPL - original licence link has changed is not relivant.
8381  *
8382  * Fork - LGPL
8383  * <script type="text/javascript">
8384  */
8385
8386 /**
8387  * @class Roo.BoxComponent
8388  * @extends Roo.Component
8389  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8390  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8391  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8392  * layout containers.
8393  * @constructor
8394  * @param {Roo.Element/String/Object} config The configuration options.
8395  */
8396 Roo.BoxComponent = function(config){
8397     Roo.Component.call(this, config);
8398     this.addEvents({
8399         /**
8400          * @event resize
8401          * Fires after the component is resized.
8402              * @param {Roo.Component} this
8403              * @param {Number} adjWidth The box-adjusted width that was set
8404              * @param {Number} adjHeight The box-adjusted height that was set
8405              * @param {Number} rawWidth The width that was originally specified
8406              * @param {Number} rawHeight The height that was originally specified
8407              */
8408         resize : true,
8409         /**
8410          * @event move
8411          * Fires after the component is moved.
8412              * @param {Roo.Component} this
8413              * @param {Number} x The new x position
8414              * @param {Number} y The new y position
8415              */
8416         move : true
8417     });
8418 };
8419
8420 Roo.extend(Roo.BoxComponent, Roo.Component, {
8421     // private, set in afterRender to signify that the component has been rendered
8422     boxReady : false,
8423     // private, used to defer height settings to subclasses
8424     deferHeight: false,
8425     /** @cfg {Number} width
8426      * width (optional) size of component
8427      */
8428      /** @cfg {Number} height
8429      * height (optional) size of component
8430      */
8431      
8432     /**
8433      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8434      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8435      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8436      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8437      * @return {Roo.BoxComponent} this
8438      */
8439     setSize : function(w, h){
8440         // support for standard size objects
8441         if(typeof w == 'object'){
8442             h = w.height;
8443             w = w.width;
8444         }
8445         // not rendered
8446         if(!this.boxReady){
8447             this.width = w;
8448             this.height = h;
8449             return this;
8450         }
8451
8452         // prevent recalcs when not needed
8453         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8454             return this;
8455         }
8456         this.lastSize = {width: w, height: h};
8457
8458         var adj = this.adjustSize(w, h);
8459         var aw = adj.width, ah = adj.height;
8460         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8461             var rz = this.getResizeEl();
8462             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8463                 rz.setSize(aw, ah);
8464             }else if(!this.deferHeight && ah !== undefined){
8465                 rz.setHeight(ah);
8466             }else if(aw !== undefined){
8467                 rz.setWidth(aw);
8468             }
8469             this.onResize(aw, ah, w, h);
8470             this.fireEvent('resize', this, aw, ah, w, h);
8471         }
8472         return this;
8473     },
8474
8475     /**
8476      * Gets the current size of the component's underlying element.
8477      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8478      */
8479     getSize : function(){
8480         return this.el.getSize();
8481     },
8482
8483     /**
8484      * Gets the current XY position of the component's underlying element.
8485      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8486      * @return {Array} The XY position of the element (e.g., [100, 200])
8487      */
8488     getPosition : function(local){
8489         if(local === true){
8490             return [this.el.getLeft(true), this.el.getTop(true)];
8491         }
8492         return this.xy || this.el.getXY();
8493     },
8494
8495     /**
8496      * Gets the current box measurements of the component's underlying element.
8497      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8498      * @returns {Object} box An object in the format {x, y, width, height}
8499      */
8500     getBox : function(local){
8501         var s = this.el.getSize();
8502         if(local){
8503             s.x = this.el.getLeft(true);
8504             s.y = this.el.getTop(true);
8505         }else{
8506             var xy = this.xy || this.el.getXY();
8507             s.x = xy[0];
8508             s.y = xy[1];
8509         }
8510         return s;
8511     },
8512
8513     /**
8514      * Sets the current box measurements of the component's underlying element.
8515      * @param {Object} box An object in the format {x, y, width, height}
8516      * @returns {Roo.BoxComponent} this
8517      */
8518     updateBox : function(box){
8519         this.setSize(box.width, box.height);
8520         this.setPagePosition(box.x, box.y);
8521         return this;
8522     },
8523
8524     // protected
8525     getResizeEl : function(){
8526         return this.resizeEl || this.el;
8527     },
8528
8529     // protected
8530     getPositionEl : function(){
8531         return this.positionEl || this.el;
8532     },
8533
8534     /**
8535      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8536      * This method fires the move event.
8537      * @param {Number} left The new left
8538      * @param {Number} top The new top
8539      * @returns {Roo.BoxComponent} this
8540      */
8541     setPosition : function(x, y){
8542         this.x = x;
8543         this.y = y;
8544         if(!this.boxReady){
8545             return this;
8546         }
8547         var adj = this.adjustPosition(x, y);
8548         var ax = adj.x, ay = adj.y;
8549
8550         var el = this.getPositionEl();
8551         if(ax !== undefined || ay !== undefined){
8552             if(ax !== undefined && ay !== undefined){
8553                 el.setLeftTop(ax, ay);
8554             }else if(ax !== undefined){
8555                 el.setLeft(ax);
8556             }else if(ay !== undefined){
8557                 el.setTop(ay);
8558             }
8559             this.onPosition(ax, ay);
8560             this.fireEvent('move', this, ax, ay);
8561         }
8562         return this;
8563     },
8564
8565     /**
8566      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8567      * This method fires the move event.
8568      * @param {Number} x The new x position
8569      * @param {Number} y The new y position
8570      * @returns {Roo.BoxComponent} this
8571      */
8572     setPagePosition : function(x, y){
8573         this.pageX = x;
8574         this.pageY = y;
8575         if(!this.boxReady){
8576             return;
8577         }
8578         if(x === undefined || y === undefined){ // cannot translate undefined points
8579             return;
8580         }
8581         var p = this.el.translatePoints(x, y);
8582         this.setPosition(p.left, p.top);
8583         return this;
8584     },
8585
8586     // private
8587     onRender : function(ct, position){
8588         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8589         if(this.resizeEl){
8590             this.resizeEl = Roo.get(this.resizeEl);
8591         }
8592         if(this.positionEl){
8593             this.positionEl = Roo.get(this.positionEl);
8594         }
8595     },
8596
8597     // private
8598     afterRender : function(){
8599         Roo.BoxComponent.superclass.afterRender.call(this);
8600         this.boxReady = true;
8601         this.setSize(this.width, this.height);
8602         if(this.x || this.y){
8603             this.setPosition(this.x, this.y);
8604         }
8605         if(this.pageX || this.pageY){
8606             this.setPagePosition(this.pageX, this.pageY);
8607         }
8608     },
8609
8610     /**
8611      * Force the component's size to recalculate based on the underlying element's current height and width.
8612      * @returns {Roo.BoxComponent} this
8613      */
8614     syncSize : function(){
8615         delete this.lastSize;
8616         this.setSize(this.el.getWidth(), this.el.getHeight());
8617         return this;
8618     },
8619
8620     /**
8621      * Called after the component is resized, this method is empty by default but can be implemented by any
8622      * subclass that needs to perform custom logic after a resize occurs.
8623      * @param {Number} adjWidth The box-adjusted width that was set
8624      * @param {Number} adjHeight The box-adjusted height that was set
8625      * @param {Number} rawWidth The width that was originally specified
8626      * @param {Number} rawHeight The height that was originally specified
8627      */
8628     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8629
8630     },
8631
8632     /**
8633      * Called after the component is moved, this method is empty by default but can be implemented by any
8634      * subclass that needs to perform custom logic after a move occurs.
8635      * @param {Number} x The new x position
8636      * @param {Number} y The new y position
8637      */
8638     onPosition : function(x, y){
8639
8640     },
8641
8642     // private
8643     adjustSize : function(w, h){
8644         if(this.autoWidth){
8645             w = 'auto';
8646         }
8647         if(this.autoHeight){
8648             h = 'auto';
8649         }
8650         return {width : w, height: h};
8651     },
8652
8653     // private
8654     adjustPosition : function(x, y){
8655         return {x : x, y: y};
8656     }
8657 });/*
8658  * Based on:
8659  * Ext JS Library 1.1.1
8660  * Copyright(c) 2006-2007, Ext JS, LLC.
8661  *
8662  * Originally Released Under LGPL - original licence link has changed is not relivant.
8663  *
8664  * Fork - LGPL
8665  * <script type="text/javascript">
8666  */
8667
8668
8669 /**
8670  * @class Roo.SplitBar
8671  * @extends Roo.util.Observable
8672  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8673  * <br><br>
8674  * Usage:
8675  * <pre><code>
8676 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8677                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8678 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8679 split.minSize = 100;
8680 split.maxSize = 600;
8681 split.animate = true;
8682 split.on('moved', splitterMoved);
8683 </code></pre>
8684  * @constructor
8685  * Create a new SplitBar
8686  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8687  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8688  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8689  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8690                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8691                         position of the SplitBar).
8692  */
8693 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8694     
8695     /** @private */
8696     this.el = Roo.get(dragElement, true);
8697     this.el.dom.unselectable = "on";
8698     /** @private */
8699     this.resizingEl = Roo.get(resizingElement, true);
8700
8701     /**
8702      * @private
8703      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8704      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8705      * @type Number
8706      */
8707     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8708     
8709     /**
8710      * The minimum size of the resizing element. (Defaults to 0)
8711      * @type Number
8712      */
8713     this.minSize = 0;
8714     
8715     /**
8716      * The maximum size of the resizing element. (Defaults to 2000)
8717      * @type Number
8718      */
8719     this.maxSize = 2000;
8720     
8721     /**
8722      * Whether to animate the transition to the new size
8723      * @type Boolean
8724      */
8725     this.animate = false;
8726     
8727     /**
8728      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8729      * @type Boolean
8730      */
8731     this.useShim = false;
8732     
8733     /** @private */
8734     this.shim = null;
8735     
8736     if(!existingProxy){
8737         /** @private */
8738         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8739     }else{
8740         this.proxy = Roo.get(existingProxy).dom;
8741     }
8742     /** @private */
8743     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8744     
8745     /** @private */
8746     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8747     
8748     /** @private */
8749     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8750     
8751     /** @private */
8752     this.dragSpecs = {};
8753     
8754     /**
8755      * @private The adapter to use to positon and resize elements
8756      */
8757     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8758     this.adapter.init(this);
8759     
8760     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8761         /** @private */
8762         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8763         this.el.addClass("x-splitbar-h");
8764     }else{
8765         /** @private */
8766         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8767         this.el.addClass("x-splitbar-v");
8768     }
8769     
8770     this.addEvents({
8771         /**
8772          * @event resize
8773          * Fires when the splitter is moved (alias for {@link #event-moved})
8774          * @param {Roo.SplitBar} this
8775          * @param {Number} newSize the new width or height
8776          */
8777         "resize" : true,
8778         /**
8779          * @event moved
8780          * Fires when the splitter is moved
8781          * @param {Roo.SplitBar} this
8782          * @param {Number} newSize the new width or height
8783          */
8784         "moved" : true,
8785         /**
8786          * @event beforeresize
8787          * Fires before the splitter is dragged
8788          * @param {Roo.SplitBar} this
8789          */
8790         "beforeresize" : true,
8791
8792         "beforeapply" : true
8793     });
8794
8795     Roo.util.Observable.call(this);
8796 };
8797
8798 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8799     onStartProxyDrag : function(x, y){
8800         this.fireEvent("beforeresize", this);
8801         if(!this.overlay){
8802             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8803             o.unselectable();
8804             o.enableDisplayMode("block");
8805             // all splitbars share the same overlay
8806             Roo.SplitBar.prototype.overlay = o;
8807         }
8808         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8809         this.overlay.show();
8810         Roo.get(this.proxy).setDisplayed("block");
8811         var size = this.adapter.getElementSize(this);
8812         this.activeMinSize = this.getMinimumSize();;
8813         this.activeMaxSize = this.getMaximumSize();;
8814         var c1 = size - this.activeMinSize;
8815         var c2 = Math.max(this.activeMaxSize - size, 0);
8816         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8817             this.dd.resetConstraints();
8818             this.dd.setXConstraint(
8819                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8820                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8821             );
8822             this.dd.setYConstraint(0, 0);
8823         }else{
8824             this.dd.resetConstraints();
8825             this.dd.setXConstraint(0, 0);
8826             this.dd.setYConstraint(
8827                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8828                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8829             );
8830          }
8831         this.dragSpecs.startSize = size;
8832         this.dragSpecs.startPoint = [x, y];
8833         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8834     },
8835     
8836     /** 
8837      * @private Called after the drag operation by the DDProxy
8838      */
8839     onEndProxyDrag : function(e){
8840         Roo.get(this.proxy).setDisplayed(false);
8841         var endPoint = Roo.lib.Event.getXY(e);
8842         if(this.overlay){
8843             this.overlay.hide();
8844         }
8845         var newSize;
8846         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8847             newSize = this.dragSpecs.startSize + 
8848                 (this.placement == Roo.SplitBar.LEFT ?
8849                     endPoint[0] - this.dragSpecs.startPoint[0] :
8850                     this.dragSpecs.startPoint[0] - endPoint[0]
8851                 );
8852         }else{
8853             newSize = this.dragSpecs.startSize + 
8854                 (this.placement == Roo.SplitBar.TOP ?
8855                     endPoint[1] - this.dragSpecs.startPoint[1] :
8856                     this.dragSpecs.startPoint[1] - endPoint[1]
8857                 );
8858         }
8859         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8860         if(newSize != this.dragSpecs.startSize){
8861             if(this.fireEvent('beforeapply', this, newSize) !== false){
8862                 this.adapter.setElementSize(this, newSize);
8863                 this.fireEvent("moved", this, newSize);
8864                 this.fireEvent("resize", this, newSize);
8865             }
8866         }
8867     },
8868     
8869     /**
8870      * Get the adapter this SplitBar uses
8871      * @return The adapter object
8872      */
8873     getAdapter : function(){
8874         return this.adapter;
8875     },
8876     
8877     /**
8878      * Set the adapter this SplitBar uses
8879      * @param {Object} adapter A SplitBar adapter object
8880      */
8881     setAdapter : function(adapter){
8882         this.adapter = adapter;
8883         this.adapter.init(this);
8884     },
8885     
8886     /**
8887      * Gets the minimum size for the resizing element
8888      * @return {Number} The minimum size
8889      */
8890     getMinimumSize : function(){
8891         return this.minSize;
8892     },
8893     
8894     /**
8895      * Sets the minimum size for the resizing element
8896      * @param {Number} minSize The minimum size
8897      */
8898     setMinimumSize : function(minSize){
8899         this.minSize = minSize;
8900     },
8901     
8902     /**
8903      * Gets the maximum size for the resizing element
8904      * @return {Number} The maximum size
8905      */
8906     getMaximumSize : function(){
8907         return this.maxSize;
8908     },
8909     
8910     /**
8911      * Sets the maximum size for the resizing element
8912      * @param {Number} maxSize The maximum size
8913      */
8914     setMaximumSize : function(maxSize){
8915         this.maxSize = maxSize;
8916     },
8917     
8918     /**
8919      * Sets the initialize size for the resizing element
8920      * @param {Number} size The initial size
8921      */
8922     setCurrentSize : function(size){
8923         var oldAnimate = this.animate;
8924         this.animate = false;
8925         this.adapter.setElementSize(this, size);
8926         this.animate = oldAnimate;
8927     },
8928     
8929     /**
8930      * Destroy this splitbar. 
8931      * @param {Boolean} removeEl True to remove the element
8932      */
8933     destroy : function(removeEl){
8934         if(this.shim){
8935             this.shim.remove();
8936         }
8937         this.dd.unreg();
8938         this.proxy.parentNode.removeChild(this.proxy);
8939         if(removeEl){
8940             this.el.remove();
8941         }
8942     }
8943 });
8944
8945 /**
8946  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
8947  */
8948 Roo.SplitBar.createProxy = function(dir){
8949     var proxy = new Roo.Element(document.createElement("div"));
8950     proxy.unselectable();
8951     var cls = 'x-splitbar-proxy';
8952     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8953     document.body.appendChild(proxy.dom);
8954     return proxy.dom;
8955 };
8956
8957 /** 
8958  * @class Roo.SplitBar.BasicLayoutAdapter
8959  * Default Adapter. It assumes the splitter and resizing element are not positioned
8960  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8961  */
8962 Roo.SplitBar.BasicLayoutAdapter = function(){
8963 };
8964
8965 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8966     // do nothing for now
8967     init : function(s){
8968     
8969     },
8970     /**
8971      * Called before drag operations to get the current size of the resizing element. 
8972      * @param {Roo.SplitBar} s The SplitBar using this adapter
8973      */
8974      getElementSize : function(s){
8975         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8976             return s.resizingEl.getWidth();
8977         }else{
8978             return s.resizingEl.getHeight();
8979         }
8980     },
8981     
8982     /**
8983      * Called after drag operations to set the size of the resizing element.
8984      * @param {Roo.SplitBar} s The SplitBar using this adapter
8985      * @param {Number} newSize The new size to set
8986      * @param {Function} onComplete A function to be invoked when resizing is complete
8987      */
8988     setElementSize : function(s, newSize, onComplete){
8989         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8990             if(!s.animate){
8991                 s.resizingEl.setWidth(newSize);
8992                 if(onComplete){
8993                     onComplete(s, newSize);
8994                 }
8995             }else{
8996                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8997             }
8998         }else{
8999             
9000             if(!s.animate){
9001                 s.resizingEl.setHeight(newSize);
9002                 if(onComplete){
9003                     onComplete(s, newSize);
9004                 }
9005             }else{
9006                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
9007             }
9008         }
9009     }
9010 };
9011
9012 /** 
9013  *@class Roo.SplitBar.AbsoluteLayoutAdapter
9014  * @extends Roo.SplitBar.BasicLayoutAdapter
9015  * Adapter that  moves the splitter element to align with the resized sizing element. 
9016  * Used with an absolute positioned SplitBar.
9017  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9018  * document.body, make sure you assign an id to the body element.
9019  */
9020 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9021     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9022     this.container = Roo.get(container);
9023 };
9024
9025 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9026     init : function(s){
9027         this.basic.init(s);
9028     },
9029     
9030     getElementSize : function(s){
9031         return this.basic.getElementSize(s);
9032     },
9033     
9034     setElementSize : function(s, newSize, onComplete){
9035         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9036     },
9037     
9038     moveSplitter : function(s){
9039         var yes = Roo.SplitBar;
9040         switch(s.placement){
9041             case yes.LEFT:
9042                 s.el.setX(s.resizingEl.getRight());
9043                 break;
9044             case yes.RIGHT:
9045                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9046                 break;
9047             case yes.TOP:
9048                 s.el.setY(s.resizingEl.getBottom());
9049                 break;
9050             case yes.BOTTOM:
9051                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9052                 break;
9053         }
9054     }
9055 };
9056
9057 /**
9058  * Orientation constant - Create a vertical SplitBar
9059  * @static
9060  * @type Number
9061  */
9062 Roo.SplitBar.VERTICAL = 1;
9063
9064 /**
9065  * Orientation constant - Create a horizontal SplitBar
9066  * @static
9067  * @type Number
9068  */
9069 Roo.SplitBar.HORIZONTAL = 2;
9070
9071 /**
9072  * Placement constant - The resizing element is to the left of the splitter element
9073  * @static
9074  * @type Number
9075  */
9076 Roo.SplitBar.LEFT = 1;
9077
9078 /**
9079  * Placement constant - The resizing element is to the right of the splitter element
9080  * @static
9081  * @type Number
9082  */
9083 Roo.SplitBar.RIGHT = 2;
9084
9085 /**
9086  * Placement constant - The resizing element is positioned above the splitter element
9087  * @static
9088  * @type Number
9089  */
9090 Roo.SplitBar.TOP = 3;
9091
9092 /**
9093  * Placement constant - The resizing element is positioned under splitter element
9094  * @static
9095  * @type Number
9096  */
9097 Roo.SplitBar.BOTTOM = 4;
9098 /*
9099  * Based on:
9100  * Ext JS Library 1.1.1
9101  * Copyright(c) 2006-2007, Ext JS, LLC.
9102  *
9103  * Originally Released Under LGPL - original licence link has changed is not relivant.
9104  *
9105  * Fork - LGPL
9106  * <script type="text/javascript">
9107  */
9108
9109 /**
9110  * @class Roo.View
9111  * @extends Roo.util.Observable
9112  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9113  * This class also supports single and multi selection modes. <br>
9114  * Create a data model bound view:
9115  <pre><code>
9116  var store = new Roo.data.Store(...);
9117
9118  var view = new Roo.View({
9119     el : "my-element",
9120     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9121  
9122     singleSelect: true,
9123     selectedClass: "ydataview-selected",
9124     store: store
9125  });
9126
9127  // listen for node click?
9128  view.on("click", function(vw, index, node, e){
9129  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9130  });
9131
9132  // load XML data
9133  dataModel.load("foobar.xml");
9134  </code></pre>
9135  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9136  * <br><br>
9137  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9138  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9139  * 
9140  * Note: old style constructor is still suported (container, template, config)
9141  * 
9142  * @constructor
9143  * Create a new View
9144  * @param {Object} config The config object
9145  * 
9146  */
9147 Roo.View = function(config, depreciated_tpl, depreciated_config){
9148     
9149     if (typeof(depreciated_tpl) == 'undefined') {
9150         // new way.. - universal constructor.
9151         Roo.apply(this, config);
9152         this.el  = Roo.get(this.el);
9153     } else {
9154         // old format..
9155         this.el  = Roo.get(config);
9156         this.tpl = depreciated_tpl;
9157         Roo.apply(this, depreciated_config);
9158     }
9159      
9160     
9161     if(typeof(this.tpl) == "string"){
9162         this.tpl = new Roo.Template(this.tpl);
9163     } else {
9164         // support xtype ctors..
9165         this.tpl = new Roo.factory(this.tpl, Roo);
9166     }
9167     
9168     
9169     this.tpl.compile();
9170    
9171
9172      
9173     /** @private */
9174     this.addEvents({
9175         /**
9176          * @event beforeclick
9177          * Fires before a click is processed. Returns false to cancel the default action.
9178          * @param {Roo.View} this
9179          * @param {Number} index The index of the target node
9180          * @param {HTMLElement} node The target node
9181          * @param {Roo.EventObject} e The raw event object
9182          */
9183             "beforeclick" : true,
9184         /**
9185          * @event click
9186          * Fires when a template node is clicked.
9187          * @param {Roo.View} this
9188          * @param {Number} index The index of the target node
9189          * @param {HTMLElement} node The target node
9190          * @param {Roo.EventObject} e The raw event object
9191          */
9192             "click" : true,
9193         /**
9194          * @event dblclick
9195          * Fires when a template node is double clicked.
9196          * @param {Roo.View} this
9197          * @param {Number} index The index of the target node
9198          * @param {HTMLElement} node The target node
9199          * @param {Roo.EventObject} e The raw event object
9200          */
9201             "dblclick" : true,
9202         /**
9203          * @event contextmenu
9204          * Fires when a template node is right clicked.
9205          * @param {Roo.View} this
9206          * @param {Number} index The index of the target node
9207          * @param {HTMLElement} node The target node
9208          * @param {Roo.EventObject} e The raw event object
9209          */
9210             "contextmenu" : true,
9211         /**
9212          * @event selectionchange
9213          * Fires when the selected nodes change.
9214          * @param {Roo.View} this
9215          * @param {Array} selections Array of the selected nodes
9216          */
9217             "selectionchange" : true,
9218     
9219         /**
9220          * @event beforeselect
9221          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9222          * @param {Roo.View} this
9223          * @param {HTMLElement} node The node to be selected
9224          * @param {Array} selections Array of currently selected nodes
9225          */
9226             "beforeselect" : true,
9227         /**
9228          * @event preparedata
9229          * Fires on every row to render, to allow you to change the data.
9230          * @param {Roo.View} this
9231          * @param {Object} data to be rendered (change this)
9232          */
9233           "preparedata" : true
9234         });
9235
9236     this.el.on({
9237         "click": this.onClick,
9238         "dblclick": this.onDblClick,
9239         "contextmenu": this.onContextMenu,
9240         scope:this
9241     });
9242
9243     this.selections = [];
9244     this.nodes = [];
9245     this.cmp = new Roo.CompositeElementLite([]);
9246     if(this.store){
9247         this.store = Roo.factory(this.store, Roo.data);
9248         this.setStore(this.store, true);
9249     }
9250     Roo.View.superclass.constructor.call(this);
9251 };
9252
9253 Roo.extend(Roo.View, Roo.util.Observable, {
9254     
9255      /**
9256      * @cfg {Roo.data.Store} store Data store to load data from.
9257      */
9258     store : false,
9259     
9260     /**
9261      * @cfg {String|Roo.Element} el The container element.
9262      */
9263     el : '',
9264     
9265     /**
9266      * @cfg {String|Roo.Template} tpl The template used by this View 
9267      */
9268     tpl : false,
9269     /**
9270      * @cfg {String} dataName the named area of the template to use as the data area
9271      *                          Works with domtemplates roo-name="name"
9272      */
9273     dataName: false,
9274     /**
9275      * @cfg {String} selectedClass The css class to add to selected nodes
9276      */
9277     selectedClass : "x-view-selected",
9278      /**
9279      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9280      */
9281     emptyText : "",
9282     /**
9283      * @cfg {Boolean} multiSelect Allow multiple selection
9284      */
9285     multiSelect : false,
9286     /**
9287      * @cfg {Boolean} singleSelect Allow single selection
9288      */
9289     singleSelect:  false,
9290     
9291     /**
9292      * @cfg {Boolean} toggleSelect - selecting 
9293      */
9294     toggleSelect : false,
9295     
9296     /**
9297      * Returns the element this view is bound to.
9298      * @return {Roo.Element}
9299      */
9300     getEl : function(){
9301         return this.el;
9302     },
9303
9304     /**
9305      * Refreshes the view.
9306      */
9307     refresh : function(){
9308         var t = this.tpl;
9309         
9310         // if we are using something like 'domtemplate', then
9311         // the what gets used is:
9312         // t.applySubtemplate(NAME, data, wrapping data..)
9313         // the outer template then get' applied with
9314         //     the store 'extra data'
9315         // and the body get's added to the
9316         //      roo-name="data" node?
9317         //      <span class='roo-tpl-{name}'></span> ?????
9318         
9319         
9320         
9321         this.clearSelections();
9322         this.el.update("");
9323         var html = [];
9324         var records = this.store.getRange();
9325         if(records.length < 1) {
9326             
9327             // is this valid??  = should it render a template??
9328             
9329             this.el.update(this.emptyText);
9330             return;
9331         }
9332         var el = this.el;
9333         if (this.dataName) {
9334             this.el.update(t.apply(this.store.meta)); //????
9335             el = this.el.child('.roo-tpl-' + this.dataName);
9336         }
9337         
9338         for(var i = 0, len = records.length; i < len; i++){
9339             var data = this.prepareData(records[i].data, i, records[i]);
9340             this.fireEvent("preparedata", this, data, i, records[i]);
9341             html[html.length] = Roo.util.Format.trim(
9342                 this.dataName ?
9343                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9344                     t.apply(data)
9345             );
9346         }
9347         
9348         
9349         
9350         el.update(html.join(""));
9351         this.nodes = el.dom.childNodes;
9352         this.updateIndexes(0);
9353     },
9354
9355     /**
9356      * Function to override to reformat the data that is sent to
9357      * the template for each node.
9358      * DEPRICATED - use the preparedata event handler.
9359      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9360      * a JSON object for an UpdateManager bound view).
9361      */
9362     prepareData : function(data, index, record)
9363     {
9364         this.fireEvent("preparedata", this, data, index, record);
9365         return data;
9366     },
9367
9368     onUpdate : function(ds, record){
9369         this.clearSelections();
9370         var index = this.store.indexOf(record);
9371         var n = this.nodes[index];
9372         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9373         n.parentNode.removeChild(n);
9374         this.updateIndexes(index, index);
9375     },
9376
9377     
9378     
9379 // --------- FIXME     
9380     onAdd : function(ds, records, index)
9381     {
9382         this.clearSelections();
9383         if(this.nodes.length == 0){
9384             this.refresh();
9385             return;
9386         }
9387         var n = this.nodes[index];
9388         for(var i = 0, len = records.length; i < len; i++){
9389             var d = this.prepareData(records[i].data, i, records[i]);
9390             if(n){
9391                 this.tpl.insertBefore(n, d);
9392             }else{
9393                 
9394                 this.tpl.append(this.el, d);
9395             }
9396         }
9397         this.updateIndexes(index);
9398     },
9399
9400     onRemove : function(ds, record, index){
9401         this.clearSelections();
9402         var el = this.dataName  ?
9403             this.el.child('.roo-tpl-' + this.dataName) :
9404             this.el; 
9405         el.dom.removeChild(this.nodes[index]);
9406         this.updateIndexes(index);
9407     },
9408
9409     /**
9410      * Refresh an individual node.
9411      * @param {Number} index
9412      */
9413     refreshNode : function(index){
9414         this.onUpdate(this.store, this.store.getAt(index));
9415     },
9416
9417     updateIndexes : function(startIndex, endIndex){
9418         var ns = this.nodes;
9419         startIndex = startIndex || 0;
9420         endIndex = endIndex || ns.length - 1;
9421         for(var i = startIndex; i <= endIndex; i++){
9422             ns[i].nodeIndex = i;
9423         }
9424     },
9425
9426     /**
9427      * Changes the data store this view uses and refresh the view.
9428      * @param {Store} store
9429      */
9430     setStore : function(store, initial){
9431         if(!initial && this.store){
9432             this.store.un("datachanged", this.refresh);
9433             this.store.un("add", this.onAdd);
9434             this.store.un("remove", this.onRemove);
9435             this.store.un("update", this.onUpdate);
9436             this.store.un("clear", this.refresh);
9437         }
9438         if(store){
9439           
9440             store.on("datachanged", this.refresh, this);
9441             store.on("add", this.onAdd, this);
9442             store.on("remove", this.onRemove, this);
9443             store.on("update", this.onUpdate, this);
9444             store.on("clear", this.refresh, this);
9445         }
9446         
9447         if(store){
9448             this.refresh();
9449         }
9450     },
9451
9452     /**
9453      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9454      * @param {HTMLElement} node
9455      * @return {HTMLElement} The template node
9456      */
9457     findItemFromChild : function(node){
9458         var el = this.dataName  ?
9459             this.el.child('.roo-tpl-' + this.dataName,true) :
9460             this.el.dom; 
9461         
9462         if(!node || node.parentNode == el){
9463                     return node;
9464             }
9465             var p = node.parentNode;
9466             while(p && p != el){
9467             if(p.parentNode == el){
9468                 return p;
9469             }
9470             p = p.parentNode;
9471         }
9472             return null;
9473     },
9474
9475     /** @ignore */
9476     onClick : function(e){
9477         var item = this.findItemFromChild(e.getTarget());
9478         if(item){
9479             var index = this.indexOf(item);
9480             if(this.onItemClick(item, index, e) !== false){
9481                 this.fireEvent("click", this, index, item, e);
9482             }
9483         }else{
9484             this.clearSelections();
9485         }
9486     },
9487
9488     /** @ignore */
9489     onContextMenu : function(e){
9490         var item = this.findItemFromChild(e.getTarget());
9491         if(item){
9492             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9493         }
9494     },
9495
9496     /** @ignore */
9497     onDblClick : function(e){
9498         var item = this.findItemFromChild(e.getTarget());
9499         if(item){
9500             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9501         }
9502     },
9503
9504     onItemClick : function(item, index, e)
9505     {
9506         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9507             return false;
9508         }
9509         if (this.toggleSelect) {
9510             var m = this.isSelected(item) ? 'unselect' : 'select';
9511             Roo.log(m);
9512             var _t = this;
9513             _t[m](item, true, false);
9514             return true;
9515         }
9516         if(this.multiSelect || this.singleSelect){
9517             if(this.multiSelect && e.shiftKey && this.lastSelection){
9518                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9519             }else{
9520                 this.select(item, this.multiSelect && e.ctrlKey);
9521                 this.lastSelection = item;
9522             }
9523             e.preventDefault();
9524         }
9525         return true;
9526     },
9527
9528     /**
9529      * Get the number of selected nodes.
9530      * @return {Number}
9531      */
9532     getSelectionCount : function(){
9533         return this.selections.length;
9534     },
9535
9536     /**
9537      * Get the currently selected nodes.
9538      * @return {Array} An array of HTMLElements
9539      */
9540     getSelectedNodes : function(){
9541         return this.selections;
9542     },
9543
9544     /**
9545      * Get the indexes of the selected nodes.
9546      * @return {Array}
9547      */
9548     getSelectedIndexes : function(){
9549         var indexes = [], s = this.selections;
9550         for(var i = 0, len = s.length; i < len; i++){
9551             indexes.push(s[i].nodeIndex);
9552         }
9553         return indexes;
9554     },
9555
9556     /**
9557      * Clear all selections
9558      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9559      */
9560     clearSelections : function(suppressEvent){
9561         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9562             this.cmp.elements = this.selections;
9563             this.cmp.removeClass(this.selectedClass);
9564             this.selections = [];
9565             if(!suppressEvent){
9566                 this.fireEvent("selectionchange", this, this.selections);
9567             }
9568         }
9569     },
9570
9571     /**
9572      * Returns true if the passed node is selected
9573      * @param {HTMLElement/Number} node The node or node index
9574      * @return {Boolean}
9575      */
9576     isSelected : function(node){
9577         var s = this.selections;
9578         if(s.length < 1){
9579             return false;
9580         }
9581         node = this.getNode(node);
9582         return s.indexOf(node) !== -1;
9583     },
9584
9585     /**
9586      * Selects nodes.
9587      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
9588      * @param {Boolean} keepExisting (optional) true to keep existing selections
9589      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9590      */
9591     select : function(nodeInfo, keepExisting, suppressEvent){
9592         if(nodeInfo instanceof Array){
9593             if(!keepExisting){
9594                 this.clearSelections(true);
9595             }
9596             for(var i = 0, len = nodeInfo.length; i < len; i++){
9597                 this.select(nodeInfo[i], true, true);
9598             }
9599             return;
9600         } 
9601         var node = this.getNode(nodeInfo);
9602         if(!node || this.isSelected(node)){
9603             return; // already selected.
9604         }
9605         if(!keepExisting){
9606             this.clearSelections(true);
9607         }
9608         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9609             Roo.fly(node).addClass(this.selectedClass);
9610             this.selections.push(node);
9611             if(!suppressEvent){
9612                 this.fireEvent("selectionchange", this, this.selections);
9613             }
9614         }
9615         
9616         
9617     },
9618       /**
9619      * Unselects nodes.
9620      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
9621      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9622      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9623      */
9624     unselect : function(nodeInfo, keepExisting, suppressEvent)
9625     {
9626         if(nodeInfo instanceof Array){
9627             Roo.each(this.selections, function(s) {
9628                 this.unselect(s, nodeInfo);
9629             }, this);
9630             return;
9631         }
9632         var node = this.getNode(nodeInfo);
9633         if(!node || !this.isSelected(node)){
9634             Roo.log("not selected");
9635             return; // not selected.
9636         }
9637         // fireevent???
9638         var ns = [];
9639         Roo.each(this.selections, function(s) {
9640             if (s == node ) {
9641                 Roo.fly(node).removeClass(this.selectedClass);
9642
9643                 return;
9644             }
9645             ns.push(s);
9646         },this);
9647         
9648         this.selections= ns;
9649         this.fireEvent("selectionchange", this, this.selections);
9650     },
9651
9652     /**
9653      * Gets a template node.
9654      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9655      * @return {HTMLElement} The node or null if it wasn't found
9656      */
9657     getNode : function(nodeInfo){
9658         if(typeof nodeInfo == "string"){
9659             return document.getElementById(nodeInfo);
9660         }else if(typeof nodeInfo == "number"){
9661             return this.nodes[nodeInfo];
9662         }
9663         return nodeInfo;
9664     },
9665
9666     /**
9667      * Gets a range template nodes.
9668      * @param {Number} startIndex
9669      * @param {Number} endIndex
9670      * @return {Array} An array of nodes
9671      */
9672     getNodes : function(start, end){
9673         var ns = this.nodes;
9674         start = start || 0;
9675         end = typeof end == "undefined" ? ns.length - 1 : end;
9676         var nodes = [];
9677         if(start <= end){
9678             for(var i = start; i <= end; i++){
9679                 nodes.push(ns[i]);
9680             }
9681         } else{
9682             for(var i = start; i >= end; i--){
9683                 nodes.push(ns[i]);
9684             }
9685         }
9686         return nodes;
9687     },
9688
9689     /**
9690      * Finds the index of the passed node
9691      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9692      * @return {Number} The index of the node or -1
9693      */
9694     indexOf : function(node){
9695         node = this.getNode(node);
9696         if(typeof node.nodeIndex == "number"){
9697             return node.nodeIndex;
9698         }
9699         var ns = this.nodes;
9700         for(var i = 0, len = ns.length; i < len; i++){
9701             if(ns[i] == node){
9702                 return i;
9703             }
9704         }
9705         return -1;
9706     }
9707 });
9708 /*
9709  * Based on:
9710  * Ext JS Library 1.1.1
9711  * Copyright(c) 2006-2007, Ext JS, LLC.
9712  *
9713  * Originally Released Under LGPL - original licence link has changed is not relivant.
9714  *
9715  * Fork - LGPL
9716  * <script type="text/javascript">
9717  */
9718
9719 /**
9720  * @class Roo.JsonView
9721  * @extends Roo.View
9722  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9723 <pre><code>
9724 var view = new Roo.JsonView({
9725     container: "my-element",
9726     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9727     multiSelect: true, 
9728     jsonRoot: "data" 
9729 });
9730
9731 // listen for node click?
9732 view.on("click", function(vw, index, node, e){
9733     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9734 });
9735
9736 // direct load of JSON data
9737 view.load("foobar.php");
9738
9739 // Example from my blog list
9740 var tpl = new Roo.Template(
9741     '&lt;div class="entry"&gt;' +
9742     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9743     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9744     "&lt;/div&gt;&lt;hr /&gt;"
9745 );
9746
9747 var moreView = new Roo.JsonView({
9748     container :  "entry-list", 
9749     template : tpl,
9750     jsonRoot: "posts"
9751 });
9752 moreView.on("beforerender", this.sortEntries, this);
9753 moreView.load({
9754     url: "/blog/get-posts.php",
9755     params: "allposts=true",
9756     text: "Loading Blog Entries..."
9757 });
9758 </code></pre>
9759
9760 * Note: old code is supported with arguments : (container, template, config)
9761
9762
9763  * @constructor
9764  * Create a new JsonView
9765  * 
9766  * @param {Object} config The config object
9767  * 
9768  */
9769 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9770     
9771     
9772     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9773
9774     var um = this.el.getUpdateManager();
9775     um.setRenderer(this);
9776     um.on("update", this.onLoad, this);
9777     um.on("failure", this.onLoadException, this);
9778
9779     /**
9780      * @event beforerender
9781      * Fires before rendering of the downloaded JSON data.
9782      * @param {Roo.JsonView} this
9783      * @param {Object} data The JSON data loaded
9784      */
9785     /**
9786      * @event load
9787      * Fires when data is loaded.
9788      * @param {Roo.JsonView} this
9789      * @param {Object} data The JSON data loaded
9790      * @param {Object} response The raw Connect response object
9791      */
9792     /**
9793      * @event loadexception
9794      * Fires when loading fails.
9795      * @param {Roo.JsonView} this
9796      * @param {Object} response The raw Connect response object
9797      */
9798     this.addEvents({
9799         'beforerender' : true,
9800         'load' : true,
9801         'loadexception' : true
9802     });
9803 };
9804 Roo.extend(Roo.JsonView, Roo.View, {
9805     /**
9806      * @type {String} The root property in the loaded JSON object that contains the data
9807      */
9808     jsonRoot : "",
9809
9810     /**
9811      * Refreshes the view.
9812      */
9813     refresh : function(){
9814         this.clearSelections();
9815         this.el.update("");
9816         var html = [];
9817         var o = this.jsonData;
9818         if(o && o.length > 0){
9819             for(var i = 0, len = o.length; i < len; i++){
9820                 var data = this.prepareData(o[i], i, o);
9821                 html[html.length] = this.tpl.apply(data);
9822             }
9823         }else{
9824             html.push(this.emptyText);
9825         }
9826         this.el.update(html.join(""));
9827         this.nodes = this.el.dom.childNodes;
9828         this.updateIndexes(0);
9829     },
9830
9831     /**
9832      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
9833      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
9834      <pre><code>
9835      view.load({
9836          url: "your-url.php",
9837          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9838          callback: yourFunction,
9839          scope: yourObject, //(optional scope)
9840          discardUrl: false,
9841          nocache: false,
9842          text: "Loading...",
9843          timeout: 30,
9844          scripts: false
9845      });
9846      </code></pre>
9847      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9848      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
9849      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
9850      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9851      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
9852      */
9853     load : function(){
9854         var um = this.el.getUpdateManager();
9855         um.update.apply(um, arguments);
9856     },
9857
9858     render : function(el, response){
9859         this.clearSelections();
9860         this.el.update("");
9861         var o;
9862         try{
9863             o = Roo.util.JSON.decode(response.responseText);
9864             if(this.jsonRoot){
9865                 
9866                 o = o[this.jsonRoot];
9867             }
9868         } catch(e){
9869         }
9870         /**
9871          * The current JSON data or null
9872          */
9873         this.jsonData = o;
9874         this.beforeRender();
9875         this.refresh();
9876     },
9877
9878 /**
9879  * Get the number of records in the current JSON dataset
9880  * @return {Number}
9881  */
9882     getCount : function(){
9883         return this.jsonData ? this.jsonData.length : 0;
9884     },
9885
9886 /**
9887  * Returns the JSON object for the specified node(s)
9888  * @param {HTMLElement/Array} node The node or an array of nodes
9889  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9890  * you get the JSON object for the node
9891  */
9892     getNodeData : function(node){
9893         if(node instanceof Array){
9894             var data = [];
9895             for(var i = 0, len = node.length; i < len; i++){
9896                 data.push(this.getNodeData(node[i]));
9897             }
9898             return data;
9899         }
9900         return this.jsonData[this.indexOf(node)] || null;
9901     },
9902
9903     beforeRender : function(){
9904         this.snapshot = this.jsonData;
9905         if(this.sortInfo){
9906             this.sort.apply(this, this.sortInfo);
9907         }
9908         this.fireEvent("beforerender", this, this.jsonData);
9909     },
9910
9911     onLoad : function(el, o){
9912         this.fireEvent("load", this, this.jsonData, o);
9913     },
9914
9915     onLoadException : function(el, o){
9916         this.fireEvent("loadexception", this, o);
9917     },
9918
9919 /**
9920  * Filter the data by a specific property.
9921  * @param {String} property A property on your JSON objects
9922  * @param {String/RegExp} value Either string that the property values
9923  * should start with, or a RegExp to test against the property
9924  */
9925     filter : function(property, value){
9926         if(this.jsonData){
9927             var data = [];
9928             var ss = this.snapshot;
9929             if(typeof value == "string"){
9930                 var vlen = value.length;
9931                 if(vlen == 0){
9932                     this.clearFilter();
9933                     return;
9934                 }
9935                 value = value.toLowerCase();
9936                 for(var i = 0, len = ss.length; i < len; i++){
9937                     var o = ss[i];
9938                     if(o[property].substr(0, vlen).toLowerCase() == value){
9939                         data.push(o);
9940                     }
9941                 }
9942             } else if(value.exec){ // regex?
9943                 for(var i = 0, len = ss.length; i < len; i++){
9944                     var o = ss[i];
9945                     if(value.test(o[property])){
9946                         data.push(o);
9947                     }
9948                 }
9949             } else{
9950                 return;
9951             }
9952             this.jsonData = data;
9953             this.refresh();
9954         }
9955     },
9956
9957 /**
9958  * Filter by a function. The passed function will be called with each
9959  * object in the current dataset. If the function returns true the value is kept,
9960  * otherwise it is filtered.
9961  * @param {Function} fn
9962  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9963  */
9964     filterBy : function(fn, scope){
9965         if(this.jsonData){
9966             var data = [];
9967             var ss = this.snapshot;
9968             for(var i = 0, len = ss.length; i < len; i++){
9969                 var o = ss[i];
9970                 if(fn.call(scope || this, o)){
9971                     data.push(o);
9972                 }
9973             }
9974             this.jsonData = data;
9975             this.refresh();
9976         }
9977     },
9978
9979 /**
9980  * Clears the current filter.
9981  */
9982     clearFilter : function(){
9983         if(this.snapshot && this.jsonData != this.snapshot){
9984             this.jsonData = this.snapshot;
9985             this.refresh();
9986         }
9987     },
9988
9989
9990 /**
9991  * Sorts the data for this view and refreshes it.
9992  * @param {String} property A property on your JSON objects to sort on
9993  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9994  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9995  */
9996     sort : function(property, dir, sortType){
9997         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9998         if(this.jsonData){
9999             var p = property;
10000             var dsc = dir && dir.toLowerCase() == "desc";
10001             var f = function(o1, o2){
10002                 var v1 = sortType ? sortType(o1[p]) : o1[p];
10003                 var v2 = sortType ? sortType(o2[p]) : o2[p];
10004                 ;
10005                 if(v1 < v2){
10006                     return dsc ? +1 : -1;
10007                 } else if(v1 > v2){
10008                     return dsc ? -1 : +1;
10009                 } else{
10010                     return 0;
10011                 }
10012             };
10013             this.jsonData.sort(f);
10014             this.refresh();
10015             if(this.jsonData != this.snapshot){
10016                 this.snapshot.sort(f);
10017             }
10018         }
10019     }
10020 });/*
10021  * Based on:
10022  * Ext JS Library 1.1.1
10023  * Copyright(c) 2006-2007, Ext JS, LLC.
10024  *
10025  * Originally Released Under LGPL - original licence link has changed is not relivant.
10026  *
10027  * Fork - LGPL
10028  * <script type="text/javascript">
10029  */
10030  
10031
10032 /**
10033  * @class Roo.ColorPalette
10034  * @extends Roo.Component
10035  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
10036  * Here's an example of typical usage:
10037  * <pre><code>
10038 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
10039 cp.render('my-div');
10040
10041 cp.on('select', function(palette, selColor){
10042     // do something with selColor
10043 });
10044 </code></pre>
10045  * @constructor
10046  * Create a new ColorPalette
10047  * @param {Object} config The config object
10048  */
10049 Roo.ColorPalette = function(config){
10050     Roo.ColorPalette.superclass.constructor.call(this, config);
10051     this.addEvents({
10052         /**
10053              * @event select
10054              * Fires when a color is selected
10055              * @param {ColorPalette} this
10056              * @param {String} color The 6-digit color hex code (without the # symbol)
10057              */
10058         select: true
10059     });
10060
10061     if(this.handler){
10062         this.on("select", this.handler, this.scope, true);
10063     }
10064 };
10065 Roo.extend(Roo.ColorPalette, Roo.Component, {
10066     /**
10067      * @cfg {String} itemCls
10068      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10069      */
10070     itemCls : "x-color-palette",
10071     /**
10072      * @cfg {String} value
10073      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10074      * the hex codes are case-sensitive.
10075      */
10076     value : null,
10077     clickEvent:'click',
10078     // private
10079     ctype: "Roo.ColorPalette",
10080
10081     /**
10082      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10083      */
10084     allowReselect : false,
10085
10086     /**
10087      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10088      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10089      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10090      * of colors with the width setting until the box is symmetrical.</p>
10091      * <p>You can override individual colors if needed:</p>
10092      * <pre><code>
10093 var cp = new Roo.ColorPalette();
10094 cp.colors[0] = "FF0000";  // change the first box to red
10095 </code></pre>
10096
10097 Or you can provide a custom array of your own for complete control:
10098 <pre><code>
10099 var cp = new Roo.ColorPalette();
10100 cp.colors = ["000000", "993300", "333300"];
10101 </code></pre>
10102      * @type Array
10103      */
10104     colors : [
10105         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10106         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10107         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10108         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10109         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10110     ],
10111
10112     // private
10113     onRender : function(container, position){
10114         var t = new Roo.MasterTemplate(
10115             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10116         );
10117         var c = this.colors;
10118         for(var i = 0, len = c.length; i < len; i++){
10119             t.add([c[i]]);
10120         }
10121         var el = document.createElement("div");
10122         el.className = this.itemCls;
10123         t.overwrite(el);
10124         container.dom.insertBefore(el, position);
10125         this.el = Roo.get(el);
10126         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10127         if(this.clickEvent != 'click'){
10128             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10129         }
10130     },
10131
10132     // private
10133     afterRender : function(){
10134         Roo.ColorPalette.superclass.afterRender.call(this);
10135         if(this.value){
10136             var s = this.value;
10137             this.value = null;
10138             this.select(s);
10139         }
10140     },
10141
10142     // private
10143     handleClick : function(e, t){
10144         e.preventDefault();
10145         if(!this.disabled){
10146             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10147             this.select(c.toUpperCase());
10148         }
10149     },
10150
10151     /**
10152      * Selects the specified color in the palette (fires the select event)
10153      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10154      */
10155     select : function(color){
10156         color = color.replace("#", "");
10157         if(color != this.value || this.allowReselect){
10158             var el = this.el;
10159             if(this.value){
10160                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10161             }
10162             el.child("a.color-"+color).addClass("x-color-palette-sel");
10163             this.value = color;
10164             this.fireEvent("select", this, color);
10165         }
10166     }
10167 });/*
10168  * Based on:
10169  * Ext JS Library 1.1.1
10170  * Copyright(c) 2006-2007, Ext JS, LLC.
10171  *
10172  * Originally Released Under LGPL - original licence link has changed is not relivant.
10173  *
10174  * Fork - LGPL
10175  * <script type="text/javascript">
10176  */
10177  
10178 /**
10179  * @class Roo.DatePicker
10180  * @extends Roo.Component
10181  * Simple date picker class.
10182  * @constructor
10183  * Create a new DatePicker
10184  * @param {Object} config The config object
10185  */
10186 Roo.DatePicker = function(config){
10187     Roo.DatePicker.superclass.constructor.call(this, config);
10188
10189     this.value = config && config.value ?
10190                  config.value.clearTime() : new Date().clearTime();
10191
10192     this.addEvents({
10193         /**
10194              * @event select
10195              * Fires when a date is selected
10196              * @param {DatePicker} this
10197              * @param {Date} date The selected date
10198              */
10199         'select': true,
10200         /**
10201              * @event monthchange
10202              * Fires when the displayed month changes 
10203              * @param {DatePicker} this
10204              * @param {Date} date The selected month
10205              */
10206         'monthchange': true
10207     });
10208
10209     if(this.handler){
10210         this.on("select", this.handler,  this.scope || this);
10211     }
10212     // build the disabledDatesRE
10213     if(!this.disabledDatesRE && this.disabledDates){
10214         var dd = this.disabledDates;
10215         var re = "(?:";
10216         for(var i = 0; i < dd.length; i++){
10217             re += dd[i];
10218             if(i != dd.length-1) re += "|";
10219         }
10220         this.disabledDatesRE = new RegExp(re + ")");
10221     }
10222 };
10223
10224 Roo.extend(Roo.DatePicker, Roo.Component, {
10225     /**
10226      * @cfg {String} todayText
10227      * The text to display on the button that selects the current date (defaults to "Today")
10228      */
10229     todayText : "Today",
10230     /**
10231      * @cfg {String} okText
10232      * The text to display on the ok button
10233      */
10234     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10235     /**
10236      * @cfg {String} cancelText
10237      * The text to display on the cancel button
10238      */
10239     cancelText : "Cancel",
10240     /**
10241      * @cfg {String} todayTip
10242      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10243      */
10244     todayTip : "{0} (Spacebar)",
10245     /**
10246      * @cfg {Date} minDate
10247      * Minimum allowable date (JavaScript date object, defaults to null)
10248      */
10249     minDate : null,
10250     /**
10251      * @cfg {Date} maxDate
10252      * Maximum allowable date (JavaScript date object, defaults to null)
10253      */
10254     maxDate : null,
10255     /**
10256      * @cfg {String} minText
10257      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10258      */
10259     minText : "This date is before the minimum date",
10260     /**
10261      * @cfg {String} maxText
10262      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10263      */
10264     maxText : "This date is after the maximum date",
10265     /**
10266      * @cfg {String} format
10267      * The default date format string which can be overriden for localization support.  The format must be
10268      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10269      */
10270     format : "m/d/y",
10271     /**
10272      * @cfg {Array} disabledDays
10273      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10274      */
10275     disabledDays : null,
10276     /**
10277      * @cfg {String} disabledDaysText
10278      * The tooltip to display when the date falls on a disabled day (defaults to "")
10279      */
10280     disabledDaysText : "",
10281     /**
10282      * @cfg {RegExp} disabledDatesRE
10283      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10284      */
10285     disabledDatesRE : null,
10286     /**
10287      * @cfg {String} disabledDatesText
10288      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10289      */
10290     disabledDatesText : "",
10291     /**
10292      * @cfg {Boolean} constrainToViewport
10293      * True to constrain the date picker to the viewport (defaults to true)
10294      */
10295     constrainToViewport : true,
10296     /**
10297      * @cfg {Array} monthNames
10298      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10299      */
10300     monthNames : Date.monthNames,
10301     /**
10302      * @cfg {Array} dayNames
10303      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10304      */
10305     dayNames : Date.dayNames,
10306     /**
10307      * @cfg {String} nextText
10308      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10309      */
10310     nextText: 'Next Month (Control+Right)',
10311     /**
10312      * @cfg {String} prevText
10313      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10314      */
10315     prevText: 'Previous Month (Control+Left)',
10316     /**
10317      * @cfg {String} monthYearText
10318      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10319      */
10320     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10321     /**
10322      * @cfg {Number} startDay
10323      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10324      */
10325     startDay : 0,
10326     /**
10327      * @cfg {Bool} showClear
10328      * Show a clear button (usefull for date form elements that can be blank.)
10329      */
10330     
10331     showClear: false,
10332     
10333     /**
10334      * Sets the value of the date field
10335      * @param {Date} value The date to set
10336      */
10337     setValue : function(value){
10338         var old = this.value;
10339         this.value = value.clearTime(true);
10340         if(this.el){
10341             this.update(this.value);
10342         }
10343     },
10344
10345     /**
10346      * Gets the current selected value of the date field
10347      * @return {Date} The selected date
10348      */
10349     getValue : function(){
10350         return this.value;
10351     },
10352
10353     // private
10354     focus : function(){
10355         if(this.el){
10356             this.update(this.activeDate);
10357         }
10358     },
10359
10360     // private
10361     onRender : function(container, position){
10362         
10363         var m = [
10364              '<table cellspacing="0">',
10365                 '<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>',
10366                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10367         var dn = this.dayNames;
10368         for(var i = 0; i < 7; i++){
10369             var d = this.startDay+i;
10370             if(d > 6){
10371                 d = d-7;
10372             }
10373             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10374         }
10375         m[m.length] = "</tr></thead><tbody><tr>";
10376         for(var i = 0; i < 42; i++) {
10377             if(i % 7 == 0 && i != 0){
10378                 m[m.length] = "</tr><tr>";
10379             }
10380             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10381         }
10382         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10383             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10384
10385         var el = document.createElement("div");
10386         el.className = "x-date-picker";
10387         el.innerHTML = m.join("");
10388
10389         container.dom.insertBefore(el, position);
10390
10391         this.el = Roo.get(el);
10392         this.eventEl = Roo.get(el.firstChild);
10393
10394         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10395             handler: this.showPrevMonth,
10396             scope: this,
10397             preventDefault:true,
10398             stopDefault:true
10399         });
10400
10401         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10402             handler: this.showNextMonth,
10403             scope: this,
10404             preventDefault:true,
10405             stopDefault:true
10406         });
10407
10408         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10409
10410         this.monthPicker = this.el.down('div.x-date-mp');
10411         this.monthPicker.enableDisplayMode('block');
10412         
10413         var kn = new Roo.KeyNav(this.eventEl, {
10414             "left" : function(e){
10415                 e.ctrlKey ?
10416                     this.showPrevMonth() :
10417                     this.update(this.activeDate.add("d", -1));
10418             },
10419
10420             "right" : function(e){
10421                 e.ctrlKey ?
10422                     this.showNextMonth() :
10423                     this.update(this.activeDate.add("d", 1));
10424             },
10425
10426             "up" : function(e){
10427                 e.ctrlKey ?
10428                     this.showNextYear() :
10429                     this.update(this.activeDate.add("d", -7));
10430             },
10431
10432             "down" : function(e){
10433                 e.ctrlKey ?
10434                     this.showPrevYear() :
10435                     this.update(this.activeDate.add("d", 7));
10436             },
10437
10438             "pageUp" : function(e){
10439                 this.showNextMonth();
10440             },
10441
10442             "pageDown" : function(e){
10443                 this.showPrevMonth();
10444             },
10445
10446             "enter" : function(e){
10447                 e.stopPropagation();
10448                 return true;
10449             },
10450
10451             scope : this
10452         });
10453
10454         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10455
10456         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10457
10458         this.el.unselectable();
10459         
10460         this.cells = this.el.select("table.x-date-inner tbody td");
10461         this.textNodes = this.el.query("table.x-date-inner tbody span");
10462
10463         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10464             text: "&#160;",
10465             tooltip: this.monthYearText
10466         });
10467
10468         this.mbtn.on('click', this.showMonthPicker, this);
10469         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10470
10471
10472         var today = (new Date()).dateFormat(this.format);
10473         
10474         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10475         if (this.showClear) {
10476             baseTb.add( new Roo.Toolbar.Fill());
10477         }
10478         baseTb.add({
10479             text: String.format(this.todayText, today),
10480             tooltip: String.format(this.todayTip, today),
10481             handler: this.selectToday,
10482             scope: this
10483         });
10484         
10485         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10486             
10487         //});
10488         if (this.showClear) {
10489             
10490             baseTb.add( new Roo.Toolbar.Fill());
10491             baseTb.add({
10492                 text: '&#160;',
10493                 cls: 'x-btn-icon x-btn-clear',
10494                 handler: function() {
10495                     //this.value = '';
10496                     this.fireEvent("select", this, '');
10497                 },
10498                 scope: this
10499             });
10500         }
10501         
10502         
10503         if(Roo.isIE){
10504             this.el.repaint();
10505         }
10506         this.update(this.value);
10507     },
10508
10509     createMonthPicker : function(){
10510         if(!this.monthPicker.dom.firstChild){
10511             var buf = ['<table border="0" cellspacing="0">'];
10512             for(var i = 0; i < 6; i++){
10513                 buf.push(
10514                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10515                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10516                     i == 0 ?
10517                     '<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>' :
10518                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10519                 );
10520             }
10521             buf.push(
10522                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10523                     this.okText,
10524                     '</button><button type="button" class="x-date-mp-cancel">',
10525                     this.cancelText,
10526                     '</button></td></tr>',
10527                 '</table>'
10528             );
10529             this.monthPicker.update(buf.join(''));
10530             this.monthPicker.on('click', this.onMonthClick, this);
10531             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10532
10533             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10534             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10535
10536             this.mpMonths.each(function(m, a, i){
10537                 i += 1;
10538                 if((i%2) == 0){
10539                     m.dom.xmonth = 5 + Math.round(i * .5);
10540                 }else{
10541                     m.dom.xmonth = Math.round((i-1) * .5);
10542                 }
10543             });
10544         }
10545     },
10546
10547     showMonthPicker : function(){
10548         this.createMonthPicker();
10549         var size = this.el.getSize();
10550         this.monthPicker.setSize(size);
10551         this.monthPicker.child('table').setSize(size);
10552
10553         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10554         this.updateMPMonth(this.mpSelMonth);
10555         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10556         this.updateMPYear(this.mpSelYear);
10557
10558         this.monthPicker.slideIn('t', {duration:.2});
10559     },
10560
10561     updateMPYear : function(y){
10562         this.mpyear = y;
10563         var ys = this.mpYears.elements;
10564         for(var i = 1; i <= 10; i++){
10565             var td = ys[i-1], y2;
10566             if((i%2) == 0){
10567                 y2 = y + Math.round(i * .5);
10568                 td.firstChild.innerHTML = y2;
10569                 td.xyear = y2;
10570             }else{
10571                 y2 = y - (5-Math.round(i * .5));
10572                 td.firstChild.innerHTML = y2;
10573                 td.xyear = y2;
10574             }
10575             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10576         }
10577     },
10578
10579     updateMPMonth : function(sm){
10580         this.mpMonths.each(function(m, a, i){
10581             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10582         });
10583     },
10584
10585     selectMPMonth: function(m){
10586         
10587     },
10588
10589     onMonthClick : function(e, t){
10590         e.stopEvent();
10591         var el = new Roo.Element(t), pn;
10592         if(el.is('button.x-date-mp-cancel')){
10593             this.hideMonthPicker();
10594         }
10595         else if(el.is('button.x-date-mp-ok')){
10596             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10597             this.hideMonthPicker();
10598         }
10599         else if(pn = el.up('td.x-date-mp-month', 2)){
10600             this.mpMonths.removeClass('x-date-mp-sel');
10601             pn.addClass('x-date-mp-sel');
10602             this.mpSelMonth = pn.dom.xmonth;
10603         }
10604         else if(pn = el.up('td.x-date-mp-year', 2)){
10605             this.mpYears.removeClass('x-date-mp-sel');
10606             pn.addClass('x-date-mp-sel');
10607             this.mpSelYear = pn.dom.xyear;
10608         }
10609         else if(el.is('a.x-date-mp-prev')){
10610             this.updateMPYear(this.mpyear-10);
10611         }
10612         else if(el.is('a.x-date-mp-next')){
10613             this.updateMPYear(this.mpyear+10);
10614         }
10615     },
10616
10617     onMonthDblClick : function(e, t){
10618         e.stopEvent();
10619         var el = new Roo.Element(t), pn;
10620         if(pn = el.up('td.x-date-mp-month', 2)){
10621             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10622             this.hideMonthPicker();
10623         }
10624         else if(pn = el.up('td.x-date-mp-year', 2)){
10625             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10626             this.hideMonthPicker();
10627         }
10628     },
10629
10630     hideMonthPicker : function(disableAnim){
10631         if(this.monthPicker){
10632             if(disableAnim === true){
10633                 this.monthPicker.hide();
10634             }else{
10635                 this.monthPicker.slideOut('t', {duration:.2});
10636             }
10637         }
10638     },
10639
10640     // private
10641     showPrevMonth : function(e){
10642         this.update(this.activeDate.add("mo", -1));
10643     },
10644
10645     // private
10646     showNextMonth : function(e){
10647         this.update(this.activeDate.add("mo", 1));
10648     },
10649
10650     // private
10651     showPrevYear : function(){
10652         this.update(this.activeDate.add("y", -1));
10653     },
10654
10655     // private
10656     showNextYear : function(){
10657         this.update(this.activeDate.add("y", 1));
10658     },
10659
10660     // private
10661     handleMouseWheel : function(e){
10662         var delta = e.getWheelDelta();
10663         if(delta > 0){
10664             this.showPrevMonth();
10665             e.stopEvent();
10666         } else if(delta < 0){
10667             this.showNextMonth();
10668             e.stopEvent();
10669         }
10670     },
10671
10672     // private
10673     handleDateClick : function(e, t){
10674         e.stopEvent();
10675         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10676             this.setValue(new Date(t.dateValue));
10677             this.fireEvent("select", this, this.value);
10678         }
10679     },
10680
10681     // private
10682     selectToday : function(){
10683         this.setValue(new Date().clearTime());
10684         this.fireEvent("select", this, this.value);
10685     },
10686
10687     // private
10688     update : function(date)
10689     {
10690         var vd = this.activeDate;
10691         this.activeDate = date;
10692         if(vd && this.el){
10693             var t = date.getTime();
10694             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10695                 this.cells.removeClass("x-date-selected");
10696                 this.cells.each(function(c){
10697                    if(c.dom.firstChild.dateValue == t){
10698                        c.addClass("x-date-selected");
10699                        setTimeout(function(){
10700                             try{c.dom.firstChild.focus();}catch(e){}
10701                        }, 50);
10702                        return false;
10703                    }
10704                 });
10705                 return;
10706             }
10707         }
10708         
10709         var days = date.getDaysInMonth();
10710         var firstOfMonth = date.getFirstDateOfMonth();
10711         var startingPos = firstOfMonth.getDay()-this.startDay;
10712
10713         if(startingPos <= this.startDay){
10714             startingPos += 7;
10715         }
10716
10717         var pm = date.add("mo", -1);
10718         var prevStart = pm.getDaysInMonth()-startingPos;
10719
10720         var cells = this.cells.elements;
10721         var textEls = this.textNodes;
10722         days += startingPos;
10723
10724         // convert everything to numbers so it's fast
10725         var day = 86400000;
10726         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10727         var today = new Date().clearTime().getTime();
10728         var sel = date.clearTime().getTime();
10729         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10730         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10731         var ddMatch = this.disabledDatesRE;
10732         var ddText = this.disabledDatesText;
10733         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10734         var ddaysText = this.disabledDaysText;
10735         var format = this.format;
10736
10737         var setCellClass = function(cal, cell){
10738             cell.title = "";
10739             var t = d.getTime();
10740             cell.firstChild.dateValue = t;
10741             if(t == today){
10742                 cell.className += " x-date-today";
10743                 cell.title = cal.todayText;
10744             }
10745             if(t == sel){
10746                 cell.className += " x-date-selected";
10747                 setTimeout(function(){
10748                     try{cell.firstChild.focus();}catch(e){}
10749                 }, 50);
10750             }
10751             // disabling
10752             if(t < min) {
10753                 cell.className = " x-date-disabled";
10754                 cell.title = cal.minText;
10755                 return;
10756             }
10757             if(t > max) {
10758                 cell.className = " x-date-disabled";
10759                 cell.title = cal.maxText;
10760                 return;
10761             }
10762             if(ddays){
10763                 if(ddays.indexOf(d.getDay()) != -1){
10764                     cell.title = ddaysText;
10765                     cell.className = " x-date-disabled";
10766                 }
10767             }
10768             if(ddMatch && format){
10769                 var fvalue = d.dateFormat(format);
10770                 if(ddMatch.test(fvalue)){
10771                     cell.title = ddText.replace("%0", fvalue);
10772                     cell.className = " x-date-disabled";
10773                 }
10774             }
10775         };
10776
10777         var i = 0;
10778         for(; i < startingPos; i++) {
10779             textEls[i].innerHTML = (++prevStart);
10780             d.setDate(d.getDate()+1);
10781             cells[i].className = "x-date-prevday";
10782             setCellClass(this, cells[i]);
10783         }
10784         for(; i < days; i++){
10785             intDay = i - startingPos + 1;
10786             textEls[i].innerHTML = (intDay);
10787             d.setDate(d.getDate()+1);
10788             cells[i].className = "x-date-active";
10789             setCellClass(this, cells[i]);
10790         }
10791         var extraDays = 0;
10792         for(; i < 42; i++) {
10793              textEls[i].innerHTML = (++extraDays);
10794              d.setDate(d.getDate()+1);
10795              cells[i].className = "x-date-nextday";
10796              setCellClass(this, cells[i]);
10797         }
10798
10799         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10800         this.fireEvent('monthchange', this, date);
10801         
10802         if(!this.internalRender){
10803             var main = this.el.dom.firstChild;
10804             var w = main.offsetWidth;
10805             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10806             Roo.fly(main).setWidth(w);
10807             this.internalRender = true;
10808             // opera does not respect the auto grow header center column
10809             // then, after it gets a width opera refuses to recalculate
10810             // without a second pass
10811             if(Roo.isOpera && !this.secondPass){
10812                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10813                 this.secondPass = true;
10814                 this.update.defer(10, this, [date]);
10815             }
10816         }
10817         
10818         
10819     }
10820 });        /*
10821  * Based on:
10822  * Ext JS Library 1.1.1
10823  * Copyright(c) 2006-2007, Ext JS, LLC.
10824  *
10825  * Originally Released Under LGPL - original licence link has changed is not relivant.
10826  *
10827  * Fork - LGPL
10828  * <script type="text/javascript">
10829  */
10830 /**
10831  * @class Roo.TabPanel
10832  * @extends Roo.util.Observable
10833  * A lightweight tab container.
10834  * <br><br>
10835  * Usage:
10836  * <pre><code>
10837 // basic tabs 1, built from existing content
10838 var tabs = new Roo.TabPanel("tabs1");
10839 tabs.addTab("script", "View Script");
10840 tabs.addTab("markup", "View Markup");
10841 tabs.activate("script");
10842
10843 // more advanced tabs, built from javascript
10844 var jtabs = new Roo.TabPanel("jtabs");
10845 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10846
10847 // set up the UpdateManager
10848 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10849 var updater = tab2.getUpdateManager();
10850 updater.setDefaultUrl("ajax1.htm");
10851 tab2.on('activate', updater.refresh, updater, true);
10852
10853 // Use setUrl for Ajax loading
10854 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10855 tab3.setUrl("ajax2.htm", null, true);
10856
10857 // Disabled tab
10858 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10859 tab4.disable();
10860
10861 jtabs.activate("jtabs-1");
10862  * </code></pre>
10863  * @constructor
10864  * Create a new TabPanel.
10865  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10866  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10867  */
10868 Roo.TabPanel = function(container, config){
10869     /**
10870     * The container element for this TabPanel.
10871     * @type Roo.Element
10872     */
10873     this.el = Roo.get(container, true);
10874     if(config){
10875         if(typeof config == "boolean"){
10876             this.tabPosition = config ? "bottom" : "top";
10877         }else{
10878             Roo.apply(this, config);
10879         }
10880     }
10881     if(this.tabPosition == "bottom"){
10882         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10883         this.el.addClass("x-tabs-bottom");
10884     }
10885     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10886     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10887     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10888     if(Roo.isIE){
10889         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10890     }
10891     if(this.tabPosition != "bottom"){
10892         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10893          * @type Roo.Element
10894          */
10895         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10896         this.el.addClass("x-tabs-top");
10897     }
10898     this.items = [];
10899
10900     this.bodyEl.setStyle("position", "relative");
10901
10902     this.active = null;
10903     this.activateDelegate = this.activate.createDelegate(this);
10904
10905     this.addEvents({
10906         /**
10907          * @event tabchange
10908          * Fires when the active tab changes
10909          * @param {Roo.TabPanel} this
10910          * @param {Roo.TabPanelItem} activePanel The new active tab
10911          */
10912         "tabchange": true,
10913         /**
10914          * @event beforetabchange
10915          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10916          * @param {Roo.TabPanel} this
10917          * @param {Object} e Set cancel to true on this object to cancel the tab change
10918          * @param {Roo.TabPanelItem} tab The tab being changed to
10919          */
10920         "beforetabchange" : true
10921     });
10922
10923     Roo.EventManager.onWindowResize(this.onResize, this);
10924     this.cpad = this.el.getPadding("lr");
10925     this.hiddenCount = 0;
10926
10927
10928     // toolbar on the tabbar support...
10929     if (this.toolbar) {
10930         var tcfg = this.toolbar;
10931         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10932         this.toolbar = new Roo.Toolbar(tcfg);
10933         if (Roo.isSafari) {
10934             var tbl = tcfg.container.child('table', true);
10935             tbl.setAttribute('width', '100%');
10936         }
10937         
10938     }
10939    
10940
10941
10942     Roo.TabPanel.superclass.constructor.call(this);
10943 };
10944
10945 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10946     /*
10947      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10948      */
10949     tabPosition : "top",
10950     /*
10951      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10952      */
10953     currentTabWidth : 0,
10954     /*
10955      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10956      */
10957     minTabWidth : 40,
10958     /*
10959      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10960      */
10961     maxTabWidth : 250,
10962     /*
10963      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10964      */
10965     preferredTabWidth : 175,
10966     /*
10967      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10968      */
10969     resizeTabs : false,
10970     /*
10971      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10972      */
10973     monitorResize : true,
10974     /*
10975      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10976      */
10977     toolbar : false,
10978
10979     /**
10980      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10981      * @param {String} id The id of the div to use <b>or create</b>
10982      * @param {String} text The text for the tab
10983      * @param {String} content (optional) Content to put in the TabPanelItem body
10984      * @param {Boolean} closable (optional) True to create a close icon on the tab
10985      * @return {Roo.TabPanelItem} The created TabPanelItem
10986      */
10987     addTab : function(id, text, content, closable){
10988         var item = new Roo.TabPanelItem(this, id, text, closable);
10989         this.addTabItem(item);
10990         if(content){
10991             item.setContent(content);
10992         }
10993         return item;
10994     },
10995
10996     /**
10997      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10998      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10999      * @return {Roo.TabPanelItem}
11000      */
11001     getTab : function(id){
11002         return this.items[id];
11003     },
11004
11005     /**
11006      * Hides the {@link Roo.TabPanelItem} with the specified id/index
11007      * @param {String/Number} id The id or index of the TabPanelItem to hide.
11008      */
11009     hideTab : function(id){
11010         var t = this.items[id];
11011         if(!t.isHidden()){
11012            t.setHidden(true);
11013            this.hiddenCount++;
11014            this.autoSizeTabs();
11015         }
11016     },
11017
11018     /**
11019      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
11020      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
11021      */
11022     unhideTab : function(id){
11023         var t = this.items[id];
11024         if(t.isHidden()){
11025            t.setHidden(false);
11026            this.hiddenCount--;
11027            this.autoSizeTabs();
11028         }
11029     },
11030
11031     /**
11032      * Adds an existing {@link Roo.TabPanelItem}.
11033      * @param {Roo.TabPanelItem} item The TabPanelItem to add
11034      */
11035     addTabItem : function(item){
11036         this.items[item.id] = item;
11037         this.items.push(item);
11038         if(this.resizeTabs){
11039            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
11040            this.autoSizeTabs();
11041         }else{
11042             item.autoSize();
11043         }
11044     },
11045
11046     /**
11047      * Removes a {@link Roo.TabPanelItem}.
11048      * @param {String/Number} id The id or index of the TabPanelItem to remove.
11049      */
11050     removeTab : function(id){
11051         var items = this.items;
11052         var tab = items[id];
11053         if(!tab) { return; }
11054         var index = items.indexOf(tab);
11055         if(this.active == tab && items.length > 1){
11056             var newTab = this.getNextAvailable(index);
11057             if(newTab) {
11058                 newTab.activate();
11059             }
11060         }
11061         this.stripEl.dom.removeChild(tab.pnode.dom);
11062         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11063             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11064         }
11065         items.splice(index, 1);
11066         delete this.items[tab.id];
11067         tab.fireEvent("close", tab);
11068         tab.purgeListeners();
11069         this.autoSizeTabs();
11070     },
11071
11072     getNextAvailable : function(start){
11073         var items = this.items;
11074         var index = start;
11075         // look for a next tab that will slide over to
11076         // replace the one being removed
11077         while(index < items.length){
11078             var item = items[++index];
11079             if(item && !item.isHidden()){
11080                 return item;
11081             }
11082         }
11083         // if one isn't found select the previous tab (on the left)
11084         index = start;
11085         while(index >= 0){
11086             var item = items[--index];
11087             if(item && !item.isHidden()){
11088                 return item;
11089             }
11090         }
11091         return null;
11092     },
11093
11094     /**
11095      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11096      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11097      */
11098     disableTab : function(id){
11099         var tab = this.items[id];
11100         if(tab && this.active != tab){
11101             tab.disable();
11102         }
11103     },
11104
11105     /**
11106      * Enables a {@link Roo.TabPanelItem} that is disabled.
11107      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11108      */
11109     enableTab : function(id){
11110         var tab = this.items[id];
11111         tab.enable();
11112     },
11113
11114     /**
11115      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11116      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11117      * @return {Roo.TabPanelItem} The TabPanelItem.
11118      */
11119     activate : function(id){
11120         var tab = this.items[id];
11121         if(!tab){
11122             return null;
11123         }
11124         if(tab == this.active || tab.disabled){
11125             return tab;
11126         }
11127         var e = {};
11128         this.fireEvent("beforetabchange", this, e, tab);
11129         if(e.cancel !== true && !tab.disabled){
11130             if(this.active){
11131                 this.active.hide();
11132             }
11133             this.active = this.items[id];
11134             this.active.show();
11135             this.fireEvent("tabchange", this, this.active);
11136         }
11137         return tab;
11138     },
11139
11140     /**
11141      * Gets the active {@link Roo.TabPanelItem}.
11142      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11143      */
11144     getActiveTab : function(){
11145         return this.active;
11146     },
11147
11148     /**
11149      * Updates the tab body element to fit the height of the container element
11150      * for overflow scrolling
11151      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11152      */
11153     syncHeight : function(targetHeight){
11154         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11155         var bm = this.bodyEl.getMargins();
11156         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11157         this.bodyEl.setHeight(newHeight);
11158         return newHeight;
11159     },
11160
11161     onResize : function(){
11162         if(this.monitorResize){
11163             this.autoSizeTabs();
11164         }
11165     },
11166
11167     /**
11168      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11169      */
11170     beginUpdate : function(){
11171         this.updating = true;
11172     },
11173
11174     /**
11175      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11176      */
11177     endUpdate : function(){
11178         this.updating = false;
11179         this.autoSizeTabs();
11180     },
11181
11182     /**
11183      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11184      */
11185     autoSizeTabs : function(){
11186         var count = this.items.length;
11187         var vcount = count - this.hiddenCount;
11188         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11189         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11190         var availWidth = Math.floor(w / vcount);
11191         var b = this.stripBody;
11192         if(b.getWidth() > w){
11193             var tabs = this.items;
11194             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11195             if(availWidth < this.minTabWidth){
11196                 /*if(!this.sleft){    // incomplete scrolling code
11197                     this.createScrollButtons();
11198                 }
11199                 this.showScroll();
11200                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11201             }
11202         }else{
11203             if(this.currentTabWidth < this.preferredTabWidth){
11204                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11205             }
11206         }
11207     },
11208
11209     /**
11210      * Returns the number of tabs in this TabPanel.
11211      * @return {Number}
11212      */
11213      getCount : function(){
11214          return this.items.length;
11215      },
11216
11217     /**
11218      * Resizes all the tabs to the passed width
11219      * @param {Number} The new width
11220      */
11221     setTabWidth : function(width){
11222         this.currentTabWidth = width;
11223         for(var i = 0, len = this.items.length; i < len; i++) {
11224                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11225         }
11226     },
11227
11228     /**
11229      * Destroys this TabPanel
11230      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11231      */
11232     destroy : function(removeEl){
11233         Roo.EventManager.removeResizeListener(this.onResize, this);
11234         for(var i = 0, len = this.items.length; i < len; i++){
11235             this.items[i].purgeListeners();
11236         }
11237         if(removeEl === true){
11238             this.el.update("");
11239             this.el.remove();
11240         }
11241     }
11242 });
11243
11244 /**
11245  * @class Roo.TabPanelItem
11246  * @extends Roo.util.Observable
11247  * Represents an individual item (tab plus body) in a TabPanel.
11248  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11249  * @param {String} id The id of this TabPanelItem
11250  * @param {String} text The text for the tab of this TabPanelItem
11251  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11252  */
11253 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11254     /**
11255      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11256      * @type Roo.TabPanel
11257      */
11258     this.tabPanel = tabPanel;
11259     /**
11260      * The id for this TabPanelItem
11261      * @type String
11262      */
11263     this.id = id;
11264     /** @private */
11265     this.disabled = false;
11266     /** @private */
11267     this.text = text;
11268     /** @private */
11269     this.loaded = false;
11270     this.closable = closable;
11271
11272     /**
11273      * The body element for this TabPanelItem.
11274      * @type Roo.Element
11275      */
11276     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11277     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11278     this.bodyEl.setStyle("display", "block");
11279     this.bodyEl.setStyle("zoom", "1");
11280     this.hideAction();
11281
11282     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11283     /** @private */
11284     this.el = Roo.get(els.el, true);
11285     this.inner = Roo.get(els.inner, true);
11286     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11287     this.pnode = Roo.get(els.el.parentNode, true);
11288     this.el.on("mousedown", this.onTabMouseDown, this);
11289     this.el.on("click", this.onTabClick, this);
11290     /** @private */
11291     if(closable){
11292         var c = Roo.get(els.close, true);
11293         c.dom.title = this.closeText;
11294         c.addClassOnOver("close-over");
11295         c.on("click", this.closeClick, this);
11296      }
11297
11298     this.addEvents({
11299          /**
11300          * @event activate
11301          * Fires when this tab becomes the active tab.
11302          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11303          * @param {Roo.TabPanelItem} this
11304          */
11305         "activate": true,
11306         /**
11307          * @event beforeclose
11308          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11309          * @param {Roo.TabPanelItem} this
11310          * @param {Object} e Set cancel to true on this object to cancel the close.
11311          */
11312         "beforeclose": true,
11313         /**
11314          * @event close
11315          * Fires when this tab is closed.
11316          * @param {Roo.TabPanelItem} this
11317          */
11318          "close": true,
11319         /**
11320          * @event deactivate
11321          * Fires when this tab is no longer the active tab.
11322          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11323          * @param {Roo.TabPanelItem} this
11324          */
11325          "deactivate" : true
11326     });
11327     this.hidden = false;
11328
11329     Roo.TabPanelItem.superclass.constructor.call(this);
11330 };
11331
11332 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11333     purgeListeners : function(){
11334        Roo.util.Observable.prototype.purgeListeners.call(this);
11335        this.el.removeAllListeners();
11336     },
11337     /**
11338      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11339      */
11340     show : function(){
11341         this.pnode.addClass("on");
11342         this.showAction();
11343         if(Roo.isOpera){
11344             this.tabPanel.stripWrap.repaint();
11345         }
11346         this.fireEvent("activate", this.tabPanel, this);
11347     },
11348
11349     /**
11350      * Returns true if this tab is the active tab.
11351      * @return {Boolean}
11352      */
11353     isActive : function(){
11354         return this.tabPanel.getActiveTab() == this;
11355     },
11356
11357     /**
11358      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11359      */
11360     hide : function(){
11361         this.pnode.removeClass("on");
11362         this.hideAction();
11363         this.fireEvent("deactivate", this.tabPanel, this);
11364     },
11365
11366     hideAction : function(){
11367         this.bodyEl.hide();
11368         this.bodyEl.setStyle("position", "absolute");
11369         this.bodyEl.setLeft("-20000px");
11370         this.bodyEl.setTop("-20000px");
11371     },
11372
11373     showAction : function(){
11374         this.bodyEl.setStyle("position", "relative");
11375         this.bodyEl.setTop("");
11376         this.bodyEl.setLeft("");
11377         this.bodyEl.show();
11378     },
11379
11380     /**
11381      * Set the tooltip for the tab.
11382      * @param {String} tooltip The tab's tooltip
11383      */
11384     setTooltip : function(text){
11385         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11386             this.textEl.dom.qtip = text;
11387             this.textEl.dom.removeAttribute('title');
11388         }else{
11389             this.textEl.dom.title = text;
11390         }
11391     },
11392
11393     onTabClick : function(e){
11394         e.preventDefault();
11395         this.tabPanel.activate(this.id);
11396     },
11397
11398     onTabMouseDown : function(e){
11399         e.preventDefault();
11400         this.tabPanel.activate(this.id);
11401     },
11402
11403     getWidth : function(){
11404         return this.inner.getWidth();
11405     },
11406
11407     setWidth : function(width){
11408         var iwidth = width - this.pnode.getPadding("lr");
11409         this.inner.setWidth(iwidth);
11410         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11411         this.pnode.setWidth(width);
11412     },
11413
11414     /**
11415      * Show or hide the tab
11416      * @param {Boolean} hidden True to hide or false to show.
11417      */
11418     setHidden : function(hidden){
11419         this.hidden = hidden;
11420         this.pnode.setStyle("display", hidden ? "none" : "");
11421     },
11422
11423     /**
11424      * Returns true if this tab is "hidden"
11425      * @return {Boolean}
11426      */
11427     isHidden : function(){
11428         return this.hidden;
11429     },
11430
11431     /**
11432      * Returns the text for this tab
11433      * @return {String}
11434      */
11435     getText : function(){
11436         return this.text;
11437     },
11438
11439     autoSize : function(){
11440         //this.el.beginMeasure();
11441         this.textEl.setWidth(1);
11442         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11443         //this.el.endMeasure();
11444     },
11445
11446     /**
11447      * Sets the text for the tab (Note: this also sets the tooltip text)
11448      * @param {String} text The tab's text and tooltip
11449      */
11450     setText : function(text){
11451         this.text = text;
11452         this.textEl.update(text);
11453         this.setTooltip(text);
11454         if(!this.tabPanel.resizeTabs){
11455             this.autoSize();
11456         }
11457     },
11458     /**
11459      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11460      */
11461     activate : function(){
11462         this.tabPanel.activate(this.id);
11463     },
11464
11465     /**
11466      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11467      */
11468     disable : function(){
11469         if(this.tabPanel.active != this){
11470             this.disabled = true;
11471             this.pnode.addClass("disabled");
11472         }
11473     },
11474
11475     /**
11476      * Enables this TabPanelItem if it was previously disabled.
11477      */
11478     enable : function(){
11479         this.disabled = false;
11480         this.pnode.removeClass("disabled");
11481     },
11482
11483     /**
11484      * Sets the content for this TabPanelItem.
11485      * @param {String} content The content
11486      * @param {Boolean} loadScripts true to look for and load scripts
11487      */
11488     setContent : function(content, loadScripts){
11489         this.bodyEl.update(content, loadScripts);
11490     },
11491
11492     /**
11493      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11494      * @return {Roo.UpdateManager} The UpdateManager
11495      */
11496     getUpdateManager : function(){
11497         return this.bodyEl.getUpdateManager();
11498     },
11499
11500     /**
11501      * Set a URL to be used to load the content for this TabPanelItem.
11502      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11503      * @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)
11504      * @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)
11505      * @return {Roo.UpdateManager} The UpdateManager
11506      */
11507     setUrl : function(url, params, loadOnce){
11508         if(this.refreshDelegate){
11509             this.un('activate', this.refreshDelegate);
11510         }
11511         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11512         this.on("activate", this.refreshDelegate);
11513         return this.bodyEl.getUpdateManager();
11514     },
11515
11516     /** @private */
11517     _handleRefresh : function(url, params, loadOnce){
11518         if(!loadOnce || !this.loaded){
11519             var updater = this.bodyEl.getUpdateManager();
11520             updater.update(url, params, this._setLoaded.createDelegate(this));
11521         }
11522     },
11523
11524     /**
11525      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11526      *   Will fail silently if the setUrl method has not been called.
11527      *   This does not activate the panel, just updates its content.
11528      */
11529     refresh : function(){
11530         if(this.refreshDelegate){
11531            this.loaded = false;
11532            this.refreshDelegate();
11533         }
11534     },
11535
11536     /** @private */
11537     _setLoaded : function(){
11538         this.loaded = true;
11539     },
11540
11541     /** @private */
11542     closeClick : function(e){
11543         var o = {};
11544         e.stopEvent();
11545         this.fireEvent("beforeclose", this, o);
11546         if(o.cancel !== true){
11547             this.tabPanel.removeTab(this.id);
11548         }
11549     },
11550     /**
11551      * The text displayed in the tooltip for the close icon.
11552      * @type String
11553      */
11554     closeText : "Close this tab"
11555 });
11556
11557 /** @private */
11558 Roo.TabPanel.prototype.createStrip = function(container){
11559     var strip = document.createElement("div");
11560     strip.className = "x-tabs-wrap";
11561     container.appendChild(strip);
11562     return strip;
11563 };
11564 /** @private */
11565 Roo.TabPanel.prototype.createStripList = function(strip){
11566     // div wrapper for retard IE
11567     // returns the "tr" element.
11568     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11569         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11570         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11571     return strip.firstChild.firstChild.firstChild.firstChild;
11572 };
11573 /** @private */
11574 Roo.TabPanel.prototype.createBody = function(container){
11575     var body = document.createElement("div");
11576     Roo.id(body, "tab-body");
11577     Roo.fly(body).addClass("x-tabs-body");
11578     container.appendChild(body);
11579     return body;
11580 };
11581 /** @private */
11582 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11583     var body = Roo.getDom(id);
11584     if(!body){
11585         body = document.createElement("div");
11586         body.id = id;
11587     }
11588     Roo.fly(body).addClass("x-tabs-item-body");
11589     bodyEl.insertBefore(body, bodyEl.firstChild);
11590     return body;
11591 };
11592 /** @private */
11593 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11594     var td = document.createElement("td");
11595     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11596     //stripEl.appendChild(td);
11597     if(closable){
11598         td.className = "x-tabs-closable";
11599         if(!this.closeTpl){
11600             this.closeTpl = new Roo.Template(
11601                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11602                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11603                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11604             );
11605         }
11606         var el = this.closeTpl.overwrite(td, {"text": text});
11607         var close = el.getElementsByTagName("div")[0];
11608         var inner = el.getElementsByTagName("em")[0];
11609         return {"el": el, "close": close, "inner": inner};
11610     } else {
11611         if(!this.tabTpl){
11612             this.tabTpl = new Roo.Template(
11613                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11614                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11615             );
11616         }
11617         var el = this.tabTpl.overwrite(td, {"text": text});
11618         var inner = el.getElementsByTagName("em")[0];
11619         return {"el": el, "inner": inner};
11620     }
11621 };/*
11622  * Based on:
11623  * Ext JS Library 1.1.1
11624  * Copyright(c) 2006-2007, Ext JS, LLC.
11625  *
11626  * Originally Released Under LGPL - original licence link has changed is not relivant.
11627  *
11628  * Fork - LGPL
11629  * <script type="text/javascript">
11630  */
11631
11632 /**
11633  * @class Roo.Button
11634  * @extends Roo.util.Observable
11635  * Simple Button class
11636  * @cfg {String} text The button text
11637  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11638  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11639  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11640  * @cfg {Object} scope The scope of the handler
11641  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11642  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11643  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11644  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11645  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11646  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11647    applies if enableToggle = true)
11648  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11649  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11650   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11651  * @constructor
11652  * Create a new button
11653  * @param {Object} config The config object
11654  */
11655 Roo.Button = function(renderTo, config)
11656 {
11657     if (!config) {
11658         config = renderTo;
11659         renderTo = config.renderTo || false;
11660     }
11661     
11662     Roo.apply(this, config);
11663     this.addEvents({
11664         /**
11665              * @event click
11666              * Fires when this button is clicked
11667              * @param {Button} this
11668              * @param {EventObject} e The click event
11669              */
11670             "click" : true,
11671         /**
11672              * @event toggle
11673              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11674              * @param {Button} this
11675              * @param {Boolean} pressed
11676              */
11677             "toggle" : true,
11678         /**
11679              * @event mouseover
11680              * Fires when the mouse hovers over the button
11681              * @param {Button} this
11682              * @param {Event} e The event object
11683              */
11684         'mouseover' : true,
11685         /**
11686              * @event mouseout
11687              * Fires when the mouse exits the button
11688              * @param {Button} this
11689              * @param {Event} e The event object
11690              */
11691         'mouseout': true,
11692          /**
11693              * @event render
11694              * Fires when the button is rendered
11695              * @param {Button} this
11696              */
11697         'render': true
11698     });
11699     if(this.menu){
11700         this.menu = Roo.menu.MenuMgr.get(this.menu);
11701     }
11702     // register listeners first!!  - so render can be captured..
11703     Roo.util.Observable.call(this);
11704     if(renderTo){
11705         this.render(renderTo);
11706     }
11707     
11708   
11709 };
11710
11711 Roo.extend(Roo.Button, Roo.util.Observable, {
11712     /**
11713      * 
11714      */
11715     
11716     /**
11717      * Read-only. True if this button is hidden
11718      * @type Boolean
11719      */
11720     hidden : false,
11721     /**
11722      * Read-only. True if this button is disabled
11723      * @type Boolean
11724      */
11725     disabled : false,
11726     /**
11727      * Read-only. True if this button is pressed (only if enableToggle = true)
11728      * @type Boolean
11729      */
11730     pressed : false,
11731
11732     /**
11733      * @cfg {Number} tabIndex 
11734      * The DOM tabIndex for this button (defaults to undefined)
11735      */
11736     tabIndex : undefined,
11737
11738     /**
11739      * @cfg {Boolean} enableToggle
11740      * True to enable pressed/not pressed toggling (defaults to false)
11741      */
11742     enableToggle: false,
11743     /**
11744      * @cfg {Mixed} menu
11745      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11746      */
11747     menu : undefined,
11748     /**
11749      * @cfg {String} menuAlign
11750      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11751      */
11752     menuAlign : "tl-bl?",
11753
11754     /**
11755      * @cfg {String} iconCls
11756      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11757      */
11758     iconCls : undefined,
11759     /**
11760      * @cfg {String} type
11761      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11762      */
11763     type : 'button',
11764
11765     // private
11766     menuClassTarget: 'tr',
11767
11768     /**
11769      * @cfg {String} clickEvent
11770      * The type of event to map to the button's event handler (defaults to 'click')
11771      */
11772     clickEvent : 'click',
11773
11774     /**
11775      * @cfg {Boolean} handleMouseEvents
11776      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11777      */
11778     handleMouseEvents : true,
11779
11780     /**
11781      * @cfg {String} tooltipType
11782      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11783      */
11784     tooltipType : 'qtip',
11785
11786     /**
11787      * @cfg {String} cls
11788      * A CSS class to apply to the button's main element.
11789      */
11790     
11791     /**
11792      * @cfg {Roo.Template} template (Optional)
11793      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11794      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11795      * require code modifications if required elements (e.g. a button) aren't present.
11796      */
11797
11798     // private
11799     render : function(renderTo){
11800         var btn;
11801         if(this.hideParent){
11802             this.parentEl = Roo.get(renderTo);
11803         }
11804         if(!this.dhconfig){
11805             if(!this.template){
11806                 if(!Roo.Button.buttonTemplate){
11807                     // hideous table template
11808                     Roo.Button.buttonTemplate = new Roo.Template(
11809                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11810                         '<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>',
11811                         "</tr></tbody></table>");
11812                 }
11813                 this.template = Roo.Button.buttonTemplate;
11814             }
11815             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11816             var btnEl = btn.child("button:first");
11817             btnEl.on('focus', this.onFocus, this);
11818             btnEl.on('blur', this.onBlur, this);
11819             if(this.cls){
11820                 btn.addClass(this.cls);
11821             }
11822             if(this.icon){
11823                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11824             }
11825             if(this.iconCls){
11826                 btnEl.addClass(this.iconCls);
11827                 if(!this.cls){
11828                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11829                 }
11830             }
11831             if(this.tabIndex !== undefined){
11832                 btnEl.dom.tabIndex = this.tabIndex;
11833             }
11834             if(this.tooltip){
11835                 if(typeof this.tooltip == 'object'){
11836                     Roo.QuickTips.tips(Roo.apply({
11837                           target: btnEl.id
11838                     }, this.tooltip));
11839                 } else {
11840                     btnEl.dom[this.tooltipType] = this.tooltip;
11841                 }
11842             }
11843         }else{
11844             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11845         }
11846         this.el = btn;
11847         if(this.id){
11848             this.el.dom.id = this.el.id = this.id;
11849         }
11850         if(this.menu){
11851             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11852             this.menu.on("show", this.onMenuShow, this);
11853             this.menu.on("hide", this.onMenuHide, this);
11854         }
11855         btn.addClass("x-btn");
11856         if(Roo.isIE && !Roo.isIE7){
11857             this.autoWidth.defer(1, this);
11858         }else{
11859             this.autoWidth();
11860         }
11861         if(this.handleMouseEvents){
11862             btn.on("mouseover", this.onMouseOver, this);
11863             btn.on("mouseout", this.onMouseOut, this);
11864             btn.on("mousedown", this.onMouseDown, this);
11865         }
11866         btn.on(this.clickEvent, this.onClick, this);
11867         //btn.on("mouseup", this.onMouseUp, this);
11868         if(this.hidden){
11869             this.hide();
11870         }
11871         if(this.disabled){
11872             this.disable();
11873         }
11874         Roo.ButtonToggleMgr.register(this);
11875         if(this.pressed){
11876             this.el.addClass("x-btn-pressed");
11877         }
11878         if(this.repeat){
11879             var repeater = new Roo.util.ClickRepeater(btn,
11880                 typeof this.repeat == "object" ? this.repeat : {}
11881             );
11882             repeater.on("click", this.onClick,  this);
11883         }
11884         
11885         this.fireEvent('render', this);
11886         
11887     },
11888     /**
11889      * Returns the button's underlying element
11890      * @return {Roo.Element} The element
11891      */
11892     getEl : function(){
11893         return this.el;  
11894     },
11895     
11896     /**
11897      * Destroys this Button and removes any listeners.
11898      */
11899     destroy : function(){
11900         Roo.ButtonToggleMgr.unregister(this);
11901         this.el.removeAllListeners();
11902         this.purgeListeners();
11903         this.el.remove();
11904     },
11905
11906     // private
11907     autoWidth : function(){
11908         if(this.el){
11909             this.el.setWidth("auto");
11910             if(Roo.isIE7 && Roo.isStrict){
11911                 var ib = this.el.child('button');
11912                 if(ib && ib.getWidth() > 20){
11913                     ib.clip();
11914                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11915                 }
11916             }
11917             if(this.minWidth){
11918                 if(this.hidden){
11919                     this.el.beginMeasure();
11920                 }
11921                 if(this.el.getWidth() < this.minWidth){
11922                     this.el.setWidth(this.minWidth);
11923                 }
11924                 if(this.hidden){
11925                     this.el.endMeasure();
11926                 }
11927             }
11928         }
11929     },
11930
11931     /**
11932      * Assigns this button's click handler
11933      * @param {Function} handler The function to call when the button is clicked
11934      * @param {Object} scope (optional) Scope for the function passed in
11935      */
11936     setHandler : function(handler, scope){
11937         this.handler = handler;
11938         this.scope = scope;  
11939     },
11940     
11941     /**
11942      * Sets this button's text
11943      * @param {String} text The button text
11944      */
11945     setText : function(text){
11946         this.text = text;
11947         if(this.el){
11948             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11949         }
11950         this.autoWidth();
11951     },
11952     
11953     /**
11954      * Gets the text for this button
11955      * @return {String} The button text
11956      */
11957     getText : function(){
11958         return this.text;  
11959     },
11960     
11961     /**
11962      * Show this button
11963      */
11964     show: function(){
11965         this.hidden = false;
11966         if(this.el){
11967             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11968         }
11969     },
11970     
11971     /**
11972      * Hide this button
11973      */
11974     hide: function(){
11975         this.hidden = true;
11976         if(this.el){
11977             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11978         }
11979     },
11980     
11981     /**
11982      * Convenience function for boolean show/hide
11983      * @param {Boolean} visible True to show, false to hide
11984      */
11985     setVisible: function(visible){
11986         if(visible) {
11987             this.show();
11988         }else{
11989             this.hide();
11990         }
11991     },
11992     
11993     /**
11994      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11995      * @param {Boolean} state (optional) Force a particular state
11996      */
11997     toggle : function(state){
11998         state = state === undefined ? !this.pressed : state;
11999         if(state != this.pressed){
12000             if(state){
12001                 this.el.addClass("x-btn-pressed");
12002                 this.pressed = true;
12003                 this.fireEvent("toggle", this, true);
12004             }else{
12005                 this.el.removeClass("x-btn-pressed");
12006                 this.pressed = false;
12007                 this.fireEvent("toggle", this, false);
12008             }
12009             if(this.toggleHandler){
12010                 this.toggleHandler.call(this.scope || this, this, state);
12011             }
12012         }
12013     },
12014     
12015     /**
12016      * Focus the button
12017      */
12018     focus : function(){
12019         this.el.child('button:first').focus();
12020     },
12021     
12022     /**
12023      * Disable this button
12024      */
12025     disable : function(){
12026         if(this.el){
12027             this.el.addClass("x-btn-disabled");
12028         }
12029         this.disabled = true;
12030     },
12031     
12032     /**
12033      * Enable this button
12034      */
12035     enable : function(){
12036         if(this.el){
12037             this.el.removeClass("x-btn-disabled");
12038         }
12039         this.disabled = false;
12040     },
12041
12042     /**
12043      * Convenience function for boolean enable/disable
12044      * @param {Boolean} enabled True to enable, false to disable
12045      */
12046     setDisabled : function(v){
12047         this[v !== true ? "enable" : "disable"]();
12048     },
12049
12050     // private
12051     onClick : function(e){
12052         if(e){
12053             e.preventDefault();
12054         }
12055         if(e.button != 0){
12056             return;
12057         }
12058         if(!this.disabled){
12059             if(this.enableToggle){
12060                 this.toggle();
12061             }
12062             if(this.menu && !this.menu.isVisible()){
12063                 this.menu.show(this.el, this.menuAlign);
12064             }
12065             this.fireEvent("click", this, e);
12066             if(this.handler){
12067                 this.el.removeClass("x-btn-over");
12068                 this.handler.call(this.scope || this, this, e);
12069             }
12070         }
12071     },
12072     // private
12073     onMouseOver : function(e){
12074         if(!this.disabled){
12075             this.el.addClass("x-btn-over");
12076             this.fireEvent('mouseover', this, e);
12077         }
12078     },
12079     // private
12080     onMouseOut : function(e){
12081         if(!e.within(this.el,  true)){
12082             this.el.removeClass("x-btn-over");
12083             this.fireEvent('mouseout', this, e);
12084         }
12085     },
12086     // private
12087     onFocus : function(e){
12088         if(!this.disabled){
12089             this.el.addClass("x-btn-focus");
12090         }
12091     },
12092     // private
12093     onBlur : function(e){
12094         this.el.removeClass("x-btn-focus");
12095     },
12096     // private
12097     onMouseDown : function(e){
12098         if(!this.disabled && e.button == 0){
12099             this.el.addClass("x-btn-click");
12100             Roo.get(document).on('mouseup', this.onMouseUp, this);
12101         }
12102     },
12103     // private
12104     onMouseUp : function(e){
12105         if(e.button == 0){
12106             this.el.removeClass("x-btn-click");
12107             Roo.get(document).un('mouseup', this.onMouseUp, this);
12108         }
12109     },
12110     // private
12111     onMenuShow : function(e){
12112         this.el.addClass("x-btn-menu-active");
12113     },
12114     // private
12115     onMenuHide : function(e){
12116         this.el.removeClass("x-btn-menu-active");
12117     }   
12118 });
12119
12120 // Private utility class used by Button
12121 Roo.ButtonToggleMgr = function(){
12122    var groups = {};
12123    
12124    function toggleGroup(btn, state){
12125        if(state){
12126            var g = groups[btn.toggleGroup];
12127            for(var i = 0, l = g.length; i < l; i++){
12128                if(g[i] != btn){
12129                    g[i].toggle(false);
12130                }
12131            }
12132        }
12133    }
12134    
12135    return {
12136        register : function(btn){
12137            if(!btn.toggleGroup){
12138                return;
12139            }
12140            var g = groups[btn.toggleGroup];
12141            if(!g){
12142                g = groups[btn.toggleGroup] = [];
12143            }
12144            g.push(btn);
12145            btn.on("toggle", toggleGroup);
12146        },
12147        
12148        unregister : function(btn){
12149            if(!btn.toggleGroup){
12150                return;
12151            }
12152            var g = groups[btn.toggleGroup];
12153            if(g){
12154                g.remove(btn);
12155                btn.un("toggle", toggleGroup);
12156            }
12157        }
12158    };
12159 }();/*
12160  * Based on:
12161  * Ext JS Library 1.1.1
12162  * Copyright(c) 2006-2007, Ext JS, LLC.
12163  *
12164  * Originally Released Under LGPL - original licence link has changed is not relivant.
12165  *
12166  * Fork - LGPL
12167  * <script type="text/javascript">
12168  */
12169  
12170 /**
12171  * @class Roo.SplitButton
12172  * @extends Roo.Button
12173  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12174  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12175  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12176  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12177  * @cfg {String} arrowTooltip The title attribute of the arrow
12178  * @constructor
12179  * Create a new menu button
12180  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12181  * @param {Object} config The config object
12182  */
12183 Roo.SplitButton = function(renderTo, config){
12184     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12185     /**
12186      * @event arrowclick
12187      * Fires when this button's arrow is clicked
12188      * @param {SplitButton} this
12189      * @param {EventObject} e The click event
12190      */
12191     this.addEvents({"arrowclick":true});
12192 };
12193
12194 Roo.extend(Roo.SplitButton, Roo.Button, {
12195     render : function(renderTo){
12196         // this is one sweet looking template!
12197         var tpl = new Roo.Template(
12198             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12199             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12200             '<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>',
12201             "</tbody></table></td><td>",
12202             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12203             '<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>',
12204             "</tbody></table></td></tr></table>"
12205         );
12206         var btn = tpl.append(renderTo, [this.text, this.type], true);
12207         var btnEl = btn.child("button");
12208         if(this.cls){
12209             btn.addClass(this.cls);
12210         }
12211         if(this.icon){
12212             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12213         }
12214         if(this.iconCls){
12215             btnEl.addClass(this.iconCls);
12216             if(!this.cls){
12217                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12218             }
12219         }
12220         this.el = btn;
12221         if(this.handleMouseEvents){
12222             btn.on("mouseover", this.onMouseOver, this);
12223             btn.on("mouseout", this.onMouseOut, this);
12224             btn.on("mousedown", this.onMouseDown, this);
12225             btn.on("mouseup", this.onMouseUp, this);
12226         }
12227         btn.on(this.clickEvent, this.onClick, this);
12228         if(this.tooltip){
12229             if(typeof this.tooltip == 'object'){
12230                 Roo.QuickTips.tips(Roo.apply({
12231                       target: btnEl.id
12232                 }, this.tooltip));
12233             } else {
12234                 btnEl.dom[this.tooltipType] = this.tooltip;
12235             }
12236         }
12237         if(this.arrowTooltip){
12238             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12239         }
12240         if(this.hidden){
12241             this.hide();
12242         }
12243         if(this.disabled){
12244             this.disable();
12245         }
12246         if(this.pressed){
12247             this.el.addClass("x-btn-pressed");
12248         }
12249         if(Roo.isIE && !Roo.isIE7){
12250             this.autoWidth.defer(1, this);
12251         }else{
12252             this.autoWidth();
12253         }
12254         if(this.menu){
12255             this.menu.on("show", this.onMenuShow, this);
12256             this.menu.on("hide", this.onMenuHide, this);
12257         }
12258         this.fireEvent('render', this);
12259     },
12260
12261     // private
12262     autoWidth : function(){
12263         if(this.el){
12264             var tbl = this.el.child("table:first");
12265             var tbl2 = this.el.child("table:last");
12266             this.el.setWidth("auto");
12267             tbl.setWidth("auto");
12268             if(Roo.isIE7 && Roo.isStrict){
12269                 var ib = this.el.child('button:first');
12270                 if(ib && ib.getWidth() > 20){
12271                     ib.clip();
12272                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12273                 }
12274             }
12275             if(this.minWidth){
12276                 if(this.hidden){
12277                     this.el.beginMeasure();
12278                 }
12279                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12280                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12281                 }
12282                 if(this.hidden){
12283                     this.el.endMeasure();
12284                 }
12285             }
12286             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12287         } 
12288     },
12289     /**
12290      * Sets this button's click handler
12291      * @param {Function} handler The function to call when the button is clicked
12292      * @param {Object} scope (optional) Scope for the function passed above
12293      */
12294     setHandler : function(handler, scope){
12295         this.handler = handler;
12296         this.scope = scope;  
12297     },
12298     
12299     /**
12300      * Sets this button's arrow click handler
12301      * @param {Function} handler The function to call when the arrow is clicked
12302      * @param {Object} scope (optional) Scope for the function passed above
12303      */
12304     setArrowHandler : function(handler, scope){
12305         this.arrowHandler = handler;
12306         this.scope = scope;  
12307     },
12308     
12309     /**
12310      * Focus the button
12311      */
12312     focus : function(){
12313         if(this.el){
12314             this.el.child("button:first").focus();
12315         }
12316     },
12317
12318     // private
12319     onClick : function(e){
12320         e.preventDefault();
12321         if(!this.disabled){
12322             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12323                 if(this.menu && !this.menu.isVisible()){
12324                     this.menu.show(this.el, this.menuAlign);
12325                 }
12326                 this.fireEvent("arrowclick", this, e);
12327                 if(this.arrowHandler){
12328                     this.arrowHandler.call(this.scope || this, this, e);
12329                 }
12330             }else{
12331                 this.fireEvent("click", this, e);
12332                 if(this.handler){
12333                     this.handler.call(this.scope || this, this, e);
12334                 }
12335             }
12336         }
12337     },
12338     // private
12339     onMouseDown : function(e){
12340         if(!this.disabled){
12341             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12342         }
12343     },
12344     // private
12345     onMouseUp : function(e){
12346         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12347     }   
12348 });
12349
12350
12351 // backwards compat
12352 Roo.MenuButton = Roo.SplitButton;/*
12353  * Based on:
12354  * Ext JS Library 1.1.1
12355  * Copyright(c) 2006-2007, Ext JS, LLC.
12356  *
12357  * Originally Released Under LGPL - original licence link has changed is not relivant.
12358  *
12359  * Fork - LGPL
12360  * <script type="text/javascript">
12361  */
12362
12363 /**
12364  * @class Roo.Toolbar
12365  * Basic Toolbar class.
12366  * @constructor
12367  * Creates a new Toolbar
12368  * @param {Object} container The config object
12369  */ 
12370 Roo.Toolbar = function(container, buttons, config)
12371 {
12372     /// old consturctor format still supported..
12373     if(container instanceof Array){ // omit the container for later rendering
12374         buttons = container;
12375         config = buttons;
12376         container = null;
12377     }
12378     if (typeof(container) == 'object' && container.xtype) {
12379         config = container;
12380         container = config.container;
12381         buttons = config.buttons || []; // not really - use items!!
12382     }
12383     var xitems = [];
12384     if (config && config.items) {
12385         xitems = config.items;
12386         delete config.items;
12387     }
12388     Roo.apply(this, config);
12389     this.buttons = buttons;
12390     
12391     if(container){
12392         this.render(container);
12393     }
12394     this.xitems = xitems;
12395     Roo.each(xitems, function(b) {
12396         this.add(b);
12397     }, this);
12398     
12399 };
12400
12401 Roo.Toolbar.prototype = {
12402     /**
12403      * @cfg {Array} items
12404      * array of button configs or elements to add (will be converted to a MixedCollection)
12405      */
12406     
12407     /**
12408      * @cfg {String/HTMLElement/Element} container
12409      * The id or element that will contain the toolbar
12410      */
12411     // private
12412     render : function(ct){
12413         this.el = Roo.get(ct);
12414         if(this.cls){
12415             this.el.addClass(this.cls);
12416         }
12417         // using a table allows for vertical alignment
12418         // 100% width is needed by Safari...
12419         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12420         this.tr = this.el.child("tr", true);
12421         var autoId = 0;
12422         this.items = new Roo.util.MixedCollection(false, function(o){
12423             return o.id || ("item" + (++autoId));
12424         });
12425         if(this.buttons){
12426             this.add.apply(this, this.buttons);
12427             delete this.buttons;
12428         }
12429     },
12430
12431     /**
12432      * Adds element(s) to the toolbar -- this function takes a variable number of 
12433      * arguments of mixed type and adds them to the toolbar.
12434      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12435      * <ul>
12436      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12437      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12438      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12439      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12440      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12441      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12442      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12443      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12444      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12445      * </ul>
12446      * @param {Mixed} arg2
12447      * @param {Mixed} etc.
12448      */
12449     add : function(){
12450         var a = arguments, l = a.length;
12451         for(var i = 0; i < l; i++){
12452             this._add(a[i]);
12453         }
12454     },
12455     // private..
12456     _add : function(el) {
12457         
12458         if (el.xtype) {
12459             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12460         }
12461         
12462         if (el.applyTo){ // some kind of form field
12463             return this.addField(el);
12464         } 
12465         if (el.render){ // some kind of Toolbar.Item
12466             return this.addItem(el);
12467         }
12468         if (typeof el == "string"){ // string
12469             if(el == "separator" || el == "-"){
12470                 return this.addSeparator();
12471             }
12472             if (el == " "){
12473                 return this.addSpacer();
12474             }
12475             if(el == "->"){
12476                 return this.addFill();
12477             }
12478             return this.addText(el);
12479             
12480         }
12481         if(el.tagName){ // element
12482             return this.addElement(el);
12483         }
12484         if(typeof el == "object"){ // must be button config?
12485             return this.addButton(el);
12486         }
12487         // and now what?!?!
12488         return false;
12489         
12490     },
12491     
12492     /**
12493      * Add an Xtype element
12494      * @param {Object} xtype Xtype Object
12495      * @return {Object} created Object
12496      */
12497     addxtype : function(e){
12498         return this.add(e);  
12499     },
12500     
12501     /**
12502      * Returns the Element for this toolbar.
12503      * @return {Roo.Element}
12504      */
12505     getEl : function(){
12506         return this.el;  
12507     },
12508     
12509     /**
12510      * Adds a separator
12511      * @return {Roo.Toolbar.Item} The separator item
12512      */
12513     addSeparator : function(){
12514         return this.addItem(new Roo.Toolbar.Separator());
12515     },
12516
12517     /**
12518      * Adds a spacer element
12519      * @return {Roo.Toolbar.Spacer} The spacer item
12520      */
12521     addSpacer : function(){
12522         return this.addItem(new Roo.Toolbar.Spacer());
12523     },
12524
12525     /**
12526      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12527      * @return {Roo.Toolbar.Fill} The fill item
12528      */
12529     addFill : function(){
12530         return this.addItem(new Roo.Toolbar.Fill());
12531     },
12532
12533     /**
12534      * Adds any standard HTML element to the toolbar
12535      * @param {String/HTMLElement/Element} el The element or id of the element to add
12536      * @return {Roo.Toolbar.Item} The element's item
12537      */
12538     addElement : function(el){
12539         return this.addItem(new Roo.Toolbar.Item(el));
12540     },
12541     /**
12542      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12543      * @type Roo.util.MixedCollection  
12544      */
12545     items : false,
12546      
12547     /**
12548      * Adds any Toolbar.Item or subclass
12549      * @param {Roo.Toolbar.Item} item
12550      * @return {Roo.Toolbar.Item} The item
12551      */
12552     addItem : function(item){
12553         var td = this.nextBlock();
12554         item.render(td);
12555         this.items.add(item);
12556         return item;
12557     },
12558     
12559     /**
12560      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12561      * @param {Object/Array} config A button config or array of configs
12562      * @return {Roo.Toolbar.Button/Array}
12563      */
12564     addButton : function(config){
12565         if(config instanceof Array){
12566             var buttons = [];
12567             for(var i = 0, len = config.length; i < len; i++) {
12568                 buttons.push(this.addButton(config[i]));
12569             }
12570             return buttons;
12571         }
12572         var b = config;
12573         if(!(config instanceof Roo.Toolbar.Button)){
12574             b = config.split ?
12575                 new Roo.Toolbar.SplitButton(config) :
12576                 new Roo.Toolbar.Button(config);
12577         }
12578         var td = this.nextBlock();
12579         b.render(td);
12580         this.items.add(b);
12581         return b;
12582     },
12583     
12584     /**
12585      * Adds text to the toolbar
12586      * @param {String} text The text to add
12587      * @return {Roo.Toolbar.Item} The element's item
12588      */
12589     addText : function(text){
12590         return this.addItem(new Roo.Toolbar.TextItem(text));
12591     },
12592     
12593     /**
12594      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12595      * @param {Number} index The index where the item is to be inserted
12596      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12597      * @return {Roo.Toolbar.Button/Item}
12598      */
12599     insertButton : function(index, item){
12600         if(item instanceof Array){
12601             var buttons = [];
12602             for(var i = 0, len = item.length; i < len; i++) {
12603                buttons.push(this.insertButton(index + i, item[i]));
12604             }
12605             return buttons;
12606         }
12607         if (!(item instanceof Roo.Toolbar.Button)){
12608            item = new Roo.Toolbar.Button(item);
12609         }
12610         var td = document.createElement("td");
12611         this.tr.insertBefore(td, this.tr.childNodes[index]);
12612         item.render(td);
12613         this.items.insert(index, item);
12614         return item;
12615     },
12616     
12617     /**
12618      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12619      * @param {Object} config
12620      * @return {Roo.Toolbar.Item} The element's item
12621      */
12622     addDom : function(config, returnEl){
12623         var td = this.nextBlock();
12624         Roo.DomHelper.overwrite(td, config);
12625         var ti = new Roo.Toolbar.Item(td.firstChild);
12626         ti.render(td);
12627         this.items.add(ti);
12628         return ti;
12629     },
12630
12631     /**
12632      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12633      * @type Roo.util.MixedCollection  
12634      */
12635     fields : false,
12636     
12637     /**
12638      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12639      * Note: the field should not have been rendered yet. For a field that has already been
12640      * rendered, use {@link #addElement}.
12641      * @param {Roo.form.Field} field
12642      * @return {Roo.ToolbarItem}
12643      */
12644      
12645       
12646     addField : function(field) {
12647         if (!this.fields) {
12648             var autoId = 0;
12649             this.fields = new Roo.util.MixedCollection(false, function(o){
12650                 return o.id || ("item" + (++autoId));
12651             });
12652
12653         }
12654         
12655         var td = this.nextBlock();
12656         field.render(td);
12657         var ti = new Roo.Toolbar.Item(td.firstChild);
12658         ti.render(td);
12659         this.items.add(ti);
12660         this.fields.add(field);
12661         return ti;
12662     },
12663     /**
12664      * Hide the toolbar
12665      * @method hide
12666      */
12667      
12668       
12669     hide : function()
12670     {
12671         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12672         this.el.child('div').hide();
12673     },
12674     /**
12675      * Show the toolbar
12676      * @method show
12677      */
12678     show : function()
12679     {
12680         this.el.child('div').show();
12681     },
12682       
12683     // private
12684     nextBlock : function(){
12685         var td = document.createElement("td");
12686         this.tr.appendChild(td);
12687         return td;
12688     },
12689
12690     // private
12691     destroy : function(){
12692         if(this.items){ // rendered?
12693             Roo.destroy.apply(Roo, this.items.items);
12694         }
12695         if(this.fields){ // rendered?
12696             Roo.destroy.apply(Roo, this.fields.items);
12697         }
12698         Roo.Element.uncache(this.el, this.tr);
12699     }
12700 };
12701
12702 /**
12703  * @class Roo.Toolbar.Item
12704  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12705  * @constructor
12706  * Creates a new Item
12707  * @param {HTMLElement} el 
12708  */
12709 Roo.Toolbar.Item = function(el){
12710     this.el = Roo.getDom(el);
12711     this.id = Roo.id(this.el);
12712     this.hidden = false;
12713 };
12714
12715 Roo.Toolbar.Item.prototype = {
12716     
12717     /**
12718      * Get this item's HTML Element
12719      * @return {HTMLElement}
12720      */
12721     getEl : function(){
12722        return this.el;  
12723     },
12724
12725     // private
12726     render : function(td){
12727         this.td = td;
12728         td.appendChild(this.el);
12729     },
12730     
12731     /**
12732      * Removes and destroys this item.
12733      */
12734     destroy : function(){
12735         this.td.parentNode.removeChild(this.td);
12736     },
12737     
12738     /**
12739      * Shows this item.
12740      */
12741     show: function(){
12742         this.hidden = false;
12743         this.td.style.display = "";
12744     },
12745     
12746     /**
12747      * Hides this item.
12748      */
12749     hide: function(){
12750         this.hidden = true;
12751         this.td.style.display = "none";
12752     },
12753     
12754     /**
12755      * Convenience function for boolean show/hide.
12756      * @param {Boolean} visible true to show/false to hide
12757      */
12758     setVisible: function(visible){
12759         if(visible) {
12760             this.show();
12761         }else{
12762             this.hide();
12763         }
12764     },
12765     
12766     /**
12767      * Try to focus this item.
12768      */
12769     focus : function(){
12770         Roo.fly(this.el).focus();
12771     },
12772     
12773     /**
12774      * Disables this item.
12775      */
12776     disable : function(){
12777         Roo.fly(this.td).addClass("x-item-disabled");
12778         this.disabled = true;
12779         this.el.disabled = true;
12780     },
12781     
12782     /**
12783      * Enables this item.
12784      */
12785     enable : function(){
12786         Roo.fly(this.td).removeClass("x-item-disabled");
12787         this.disabled = false;
12788         this.el.disabled = false;
12789     }
12790 };
12791
12792
12793 /**
12794  * @class Roo.Toolbar.Separator
12795  * @extends Roo.Toolbar.Item
12796  * A simple toolbar separator class
12797  * @constructor
12798  * Creates a new Separator
12799  */
12800 Roo.Toolbar.Separator = function(){
12801     var s = document.createElement("span");
12802     s.className = "ytb-sep";
12803     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12804 };
12805 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12806     enable:Roo.emptyFn,
12807     disable:Roo.emptyFn,
12808     focus:Roo.emptyFn
12809 });
12810
12811 /**
12812  * @class Roo.Toolbar.Spacer
12813  * @extends Roo.Toolbar.Item
12814  * A simple element that adds extra horizontal space to a toolbar.
12815  * @constructor
12816  * Creates a new Spacer
12817  */
12818 Roo.Toolbar.Spacer = function(){
12819     var s = document.createElement("div");
12820     s.className = "ytb-spacer";
12821     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12822 };
12823 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12824     enable:Roo.emptyFn,
12825     disable:Roo.emptyFn,
12826     focus:Roo.emptyFn
12827 });
12828
12829 /**
12830  * @class Roo.Toolbar.Fill
12831  * @extends Roo.Toolbar.Spacer
12832  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12833  * @constructor
12834  * Creates a new Spacer
12835  */
12836 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12837     // private
12838     render : function(td){
12839         td.style.width = '100%';
12840         Roo.Toolbar.Fill.superclass.render.call(this, td);
12841     }
12842 });
12843
12844 /**
12845  * @class Roo.Toolbar.TextItem
12846  * @extends Roo.Toolbar.Item
12847  * A simple class that renders text directly into a toolbar.
12848  * @constructor
12849  * Creates a new TextItem
12850  * @param {String} text
12851  */
12852 Roo.Toolbar.TextItem = function(text){
12853     if (typeof(text) == 'object') {
12854         text = text.text;
12855     }
12856     var s = document.createElement("span");
12857     s.className = "ytb-text";
12858     s.innerHTML = text;
12859     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12860 };
12861 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12862     enable:Roo.emptyFn,
12863     disable:Roo.emptyFn,
12864     focus:Roo.emptyFn
12865 });
12866
12867 /**
12868  * @class Roo.Toolbar.Button
12869  * @extends Roo.Button
12870  * A button that renders into a toolbar.
12871  * @constructor
12872  * Creates a new Button
12873  * @param {Object} config A standard {@link Roo.Button} config object
12874  */
12875 Roo.Toolbar.Button = function(config){
12876     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12877 };
12878 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12879     render : function(td){
12880         this.td = td;
12881         Roo.Toolbar.Button.superclass.render.call(this, td);
12882     },
12883     
12884     /**
12885      * Removes and destroys this button
12886      */
12887     destroy : function(){
12888         Roo.Toolbar.Button.superclass.destroy.call(this);
12889         this.td.parentNode.removeChild(this.td);
12890     },
12891     
12892     /**
12893      * Shows this button
12894      */
12895     show: function(){
12896         this.hidden = false;
12897         this.td.style.display = "";
12898     },
12899     
12900     /**
12901      * Hides this button
12902      */
12903     hide: function(){
12904         this.hidden = true;
12905         this.td.style.display = "none";
12906     },
12907
12908     /**
12909      * Disables this item
12910      */
12911     disable : function(){
12912         Roo.fly(this.td).addClass("x-item-disabled");
12913         this.disabled = true;
12914     },
12915
12916     /**
12917      * Enables this item
12918      */
12919     enable : function(){
12920         Roo.fly(this.td).removeClass("x-item-disabled");
12921         this.disabled = false;
12922     }
12923 });
12924 // backwards compat
12925 Roo.ToolbarButton = Roo.Toolbar.Button;
12926
12927 /**
12928  * @class Roo.Toolbar.SplitButton
12929  * @extends Roo.SplitButton
12930  * A menu button that renders into a toolbar.
12931  * @constructor
12932  * Creates a new SplitButton
12933  * @param {Object} config A standard {@link Roo.SplitButton} config object
12934  */
12935 Roo.Toolbar.SplitButton = function(config){
12936     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12937 };
12938 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12939     render : function(td){
12940         this.td = td;
12941         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12942     },
12943     
12944     /**
12945      * Removes and destroys this button
12946      */
12947     destroy : function(){
12948         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12949         this.td.parentNode.removeChild(this.td);
12950     },
12951     
12952     /**
12953      * Shows this button
12954      */
12955     show: function(){
12956         this.hidden = false;
12957         this.td.style.display = "";
12958     },
12959     
12960     /**
12961      * Hides this button
12962      */
12963     hide: function(){
12964         this.hidden = true;
12965         this.td.style.display = "none";
12966     }
12967 });
12968
12969 // backwards compat
12970 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12971  * Based on:
12972  * Ext JS Library 1.1.1
12973  * Copyright(c) 2006-2007, Ext JS, LLC.
12974  *
12975  * Originally Released Under LGPL - original licence link has changed is not relivant.
12976  *
12977  * Fork - LGPL
12978  * <script type="text/javascript">
12979  */
12980  
12981 /**
12982  * @class Roo.PagingToolbar
12983  * @extends Roo.Toolbar
12984  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12985  * @constructor
12986  * Create a new PagingToolbar
12987  * @param {Object} config The config object
12988  */
12989 Roo.PagingToolbar = function(el, ds, config)
12990 {
12991     // old args format still supported... - xtype is prefered..
12992     if (typeof(el) == 'object' && el.xtype) {
12993         // created from xtype...
12994         config = el;
12995         ds = el.dataSource;
12996         el = config.container;
12997     }
12998     var items = [];
12999     if (config.items) {
13000         items = config.items;
13001         config.items = [];
13002     }
13003     
13004     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
13005     this.ds = ds;
13006     this.cursor = 0;
13007     this.renderButtons(this.el);
13008     this.bind(ds);
13009     
13010     // supprot items array.
13011    
13012     Roo.each(items, function(e) {
13013         this.add(Roo.factory(e));
13014     },this);
13015     
13016 };
13017
13018 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13019     /**
13020      * @cfg {Roo.data.Store} dataSource
13021      * The underlying data store providing the paged data
13022      */
13023     /**
13024      * @cfg {String/HTMLElement/Element} container
13025      * container The id or element that will contain the toolbar
13026      */
13027     /**
13028      * @cfg {Boolean} displayInfo
13029      * True to display the displayMsg (defaults to false)
13030      */
13031     /**
13032      * @cfg {Number} pageSize
13033      * The number of records to display per page (defaults to 20)
13034      */
13035     pageSize: 20,
13036     /**
13037      * @cfg {String} displayMsg
13038      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13039      */
13040     displayMsg : 'Displaying {0} - {1} of {2}',
13041     /**
13042      * @cfg {String} emptyMsg
13043      * The message to display when no records are found (defaults to "No data to display")
13044      */
13045     emptyMsg : 'No data to display',
13046     /**
13047      * Customizable piece of the default paging text (defaults to "Page")
13048      * @type String
13049      */
13050     beforePageText : "Page",
13051     /**
13052      * Customizable piece of the default paging text (defaults to "of %0")
13053      * @type String
13054      */
13055     afterPageText : "of {0}",
13056     /**
13057      * Customizable piece of the default paging text (defaults to "First Page")
13058      * @type String
13059      */
13060     firstText : "First Page",
13061     /**
13062      * Customizable piece of the default paging text (defaults to "Previous Page")
13063      * @type String
13064      */
13065     prevText : "Previous Page",
13066     /**
13067      * Customizable piece of the default paging text (defaults to "Next Page")
13068      * @type String
13069      */
13070     nextText : "Next Page",
13071     /**
13072      * Customizable piece of the default paging text (defaults to "Last Page")
13073      * @type String
13074      */
13075     lastText : "Last Page",
13076     /**
13077      * Customizable piece of the default paging text (defaults to "Refresh")
13078      * @type String
13079      */
13080     refreshText : "Refresh",
13081
13082     // private
13083     renderButtons : function(el){
13084         Roo.PagingToolbar.superclass.render.call(this, el);
13085         this.first = this.addButton({
13086             tooltip: this.firstText,
13087             cls: "x-btn-icon x-grid-page-first",
13088             disabled: true,
13089             handler: this.onClick.createDelegate(this, ["first"])
13090         });
13091         this.prev = this.addButton({
13092             tooltip: this.prevText,
13093             cls: "x-btn-icon x-grid-page-prev",
13094             disabled: true,
13095             handler: this.onClick.createDelegate(this, ["prev"])
13096         });
13097         //this.addSeparator();
13098         this.add(this.beforePageText);
13099         this.field = Roo.get(this.addDom({
13100            tag: "input",
13101            type: "text",
13102            size: "3",
13103            value: "1",
13104            cls: "x-grid-page-number"
13105         }).el);
13106         this.field.on("keydown", this.onPagingKeydown, this);
13107         this.field.on("focus", function(){this.dom.select();});
13108         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13109         this.field.setHeight(18);
13110         //this.addSeparator();
13111         this.next = this.addButton({
13112             tooltip: this.nextText,
13113             cls: "x-btn-icon x-grid-page-next",
13114             disabled: true,
13115             handler: this.onClick.createDelegate(this, ["next"])
13116         });
13117         this.last = this.addButton({
13118             tooltip: this.lastText,
13119             cls: "x-btn-icon x-grid-page-last",
13120             disabled: true,
13121             handler: this.onClick.createDelegate(this, ["last"])
13122         });
13123         //this.addSeparator();
13124         this.loading = this.addButton({
13125             tooltip: this.refreshText,
13126             cls: "x-btn-icon x-grid-loading",
13127             handler: this.onClick.createDelegate(this, ["refresh"])
13128         });
13129
13130         if(this.displayInfo){
13131             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13132         }
13133     },
13134
13135     // private
13136     updateInfo : function(){
13137         if(this.displayEl){
13138             var count = this.ds.getCount();
13139             var msg = count == 0 ?
13140                 this.emptyMsg :
13141                 String.format(
13142                     this.displayMsg,
13143                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13144                 );
13145             this.displayEl.update(msg);
13146         }
13147     },
13148
13149     // private
13150     onLoad : function(ds, r, o){
13151        this.cursor = o.params ? o.params.start : 0;
13152        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13153
13154        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13155        this.field.dom.value = ap;
13156        this.first.setDisabled(ap == 1);
13157        this.prev.setDisabled(ap == 1);
13158        this.next.setDisabled(ap == ps);
13159        this.last.setDisabled(ap == ps);
13160        this.loading.enable();
13161        this.updateInfo();
13162     },
13163
13164     // private
13165     getPageData : function(){
13166         var total = this.ds.getTotalCount();
13167         return {
13168             total : total,
13169             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13170             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13171         };
13172     },
13173
13174     // private
13175     onLoadError : function(){
13176         this.loading.enable();
13177     },
13178
13179     // private
13180     onPagingKeydown : function(e){
13181         var k = e.getKey();
13182         var d = this.getPageData();
13183         if(k == e.RETURN){
13184             var v = this.field.dom.value, pageNum;
13185             if(!v || isNaN(pageNum = parseInt(v, 10))){
13186                 this.field.dom.value = d.activePage;
13187                 return;
13188             }
13189             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13190             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13191             e.stopEvent();
13192         }
13193         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))
13194         {
13195           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13196           this.field.dom.value = pageNum;
13197           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13198           e.stopEvent();
13199         }
13200         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13201         {
13202           var v = this.field.dom.value, pageNum; 
13203           var increment = (e.shiftKey) ? 10 : 1;
13204           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13205             increment *= -1;
13206           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13207             this.field.dom.value = d.activePage;
13208             return;
13209           }
13210           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13211           {
13212             this.field.dom.value = parseInt(v, 10) + increment;
13213             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13214             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13215           }
13216           e.stopEvent();
13217         }
13218     },
13219
13220     // private
13221     beforeLoad : function(){
13222         if(this.loading){
13223             this.loading.disable();
13224         }
13225     },
13226
13227     // private
13228     onClick : function(which){
13229         var ds = this.ds;
13230         switch(which){
13231             case "first":
13232                 ds.load({params:{start: 0, limit: this.pageSize}});
13233             break;
13234             case "prev":
13235                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13236             break;
13237             case "next":
13238                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13239             break;
13240             case "last":
13241                 var total = ds.getTotalCount();
13242                 var extra = total % this.pageSize;
13243                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13244                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13245             break;
13246             case "refresh":
13247                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13248             break;
13249         }
13250     },
13251
13252     /**
13253      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13254      * @param {Roo.data.Store} store The data store to unbind
13255      */
13256     unbind : function(ds){
13257         ds.un("beforeload", this.beforeLoad, this);
13258         ds.un("load", this.onLoad, this);
13259         ds.un("loadexception", this.onLoadError, this);
13260         ds.un("remove", this.updateInfo, this);
13261         ds.un("add", this.updateInfo, this);
13262         this.ds = undefined;
13263     },
13264
13265     /**
13266      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13267      * @param {Roo.data.Store} store The data store to bind
13268      */
13269     bind : function(ds){
13270         ds.on("beforeload", this.beforeLoad, this);
13271         ds.on("load", this.onLoad, this);
13272         ds.on("loadexception", this.onLoadError, this);
13273         ds.on("remove", this.updateInfo, this);
13274         ds.on("add", this.updateInfo, this);
13275         this.ds = ds;
13276     }
13277 });/*
13278  * Based on:
13279  * Ext JS Library 1.1.1
13280  * Copyright(c) 2006-2007, Ext JS, LLC.
13281  *
13282  * Originally Released Under LGPL - original licence link has changed is not relivant.
13283  *
13284  * Fork - LGPL
13285  * <script type="text/javascript">
13286  */
13287
13288 /**
13289  * @class Roo.Resizable
13290  * @extends Roo.util.Observable
13291  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13292  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13293  * 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
13294  * the element will be wrapped for you automatically.</p>
13295  * <p>Here is the list of valid resize handles:</p>
13296  * <pre>
13297 Value   Description
13298 ------  -------------------
13299  'n'     north
13300  's'     south
13301  'e'     east
13302  'w'     west
13303  'nw'    northwest
13304  'sw'    southwest
13305  'se'    southeast
13306  'ne'    northeast
13307  'hd'    horizontal drag
13308  'all'   all
13309 </pre>
13310  * <p>Here's an example showing the creation of a typical Resizable:</p>
13311  * <pre><code>
13312 var resizer = new Roo.Resizable("element-id", {
13313     handles: 'all',
13314     minWidth: 200,
13315     minHeight: 100,
13316     maxWidth: 500,
13317     maxHeight: 400,
13318     pinned: true
13319 });
13320 resizer.on("resize", myHandler);
13321 </code></pre>
13322  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13323  * resizer.east.setDisplayed(false);</p>
13324  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13325  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13326  * resize operation's new size (defaults to [0, 0])
13327  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13328  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13329  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13330  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13331  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13332  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13333  * @cfg {Number} width The width of the element in pixels (defaults to null)
13334  * @cfg {Number} height The height of the element in pixels (defaults to null)
13335  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13336  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13337  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13338  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13339  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13340  * in favor of the handles config option (defaults to false)
13341  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13342  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13343  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13344  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13345  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13346  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13347  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13348  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13349  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13350  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13351  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13352  * @constructor
13353  * Create a new resizable component
13354  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13355  * @param {Object} config configuration options
13356   */
13357 Roo.Resizable = function(el, config)
13358 {
13359     this.el = Roo.get(el);
13360
13361     if(config && config.wrap){
13362         config.resizeChild = this.el;
13363         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13364         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13365         this.el.setStyle("overflow", "hidden");
13366         this.el.setPositioning(config.resizeChild.getPositioning());
13367         config.resizeChild.clearPositioning();
13368         if(!config.width || !config.height){
13369             var csize = config.resizeChild.getSize();
13370             this.el.setSize(csize.width, csize.height);
13371         }
13372         if(config.pinned && !config.adjustments){
13373             config.adjustments = "auto";
13374         }
13375     }
13376
13377     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13378     this.proxy.unselectable();
13379     this.proxy.enableDisplayMode('block');
13380
13381     Roo.apply(this, config);
13382
13383     if(this.pinned){
13384         this.disableTrackOver = true;
13385         this.el.addClass("x-resizable-pinned");
13386     }
13387     // if the element isn't positioned, make it relative
13388     var position = this.el.getStyle("position");
13389     if(position != "absolute" && position != "fixed"){
13390         this.el.setStyle("position", "relative");
13391     }
13392     if(!this.handles){ // no handles passed, must be legacy style
13393         this.handles = 's,e,se';
13394         if(this.multiDirectional){
13395             this.handles += ',n,w';
13396         }
13397     }
13398     if(this.handles == "all"){
13399         this.handles = "n s e w ne nw se sw";
13400     }
13401     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13402     var ps = Roo.Resizable.positions;
13403     for(var i = 0, len = hs.length; i < len; i++){
13404         if(hs[i] && ps[hs[i]]){
13405             var pos = ps[hs[i]];
13406             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13407         }
13408     }
13409     // legacy
13410     this.corner = this.southeast;
13411     
13412     // updateBox = the box can move..
13413     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13414         this.updateBox = true;
13415     }
13416
13417     this.activeHandle = null;
13418
13419     if(this.resizeChild){
13420         if(typeof this.resizeChild == "boolean"){
13421             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13422         }else{
13423             this.resizeChild = Roo.get(this.resizeChild, true);
13424         }
13425     }
13426     
13427     if(this.adjustments == "auto"){
13428         var rc = this.resizeChild;
13429         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13430         if(rc && (hw || hn)){
13431             rc.position("relative");
13432             rc.setLeft(hw ? hw.el.getWidth() : 0);
13433             rc.setTop(hn ? hn.el.getHeight() : 0);
13434         }
13435         this.adjustments = [
13436             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13437             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13438         ];
13439     }
13440
13441     if(this.draggable){
13442         this.dd = this.dynamic ?
13443             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13444         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13445     }
13446
13447     // public events
13448     this.addEvents({
13449         /**
13450          * @event beforeresize
13451          * Fired before resize is allowed. Set enabled to false to cancel resize.
13452          * @param {Roo.Resizable} this
13453          * @param {Roo.EventObject} e The mousedown event
13454          */
13455         "beforeresize" : true,
13456         /**
13457          * @event resize
13458          * Fired after a resize.
13459          * @param {Roo.Resizable} this
13460          * @param {Number} width The new width
13461          * @param {Number} height The new height
13462          * @param {Roo.EventObject} e The mouseup event
13463          */
13464         "resize" : true
13465     });
13466
13467     if(this.width !== null && this.height !== null){
13468         this.resizeTo(this.width, this.height);
13469     }else{
13470         this.updateChildSize();
13471     }
13472     if(Roo.isIE){
13473         this.el.dom.style.zoom = 1;
13474     }
13475     Roo.Resizable.superclass.constructor.call(this);
13476 };
13477
13478 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13479         resizeChild : false,
13480         adjustments : [0, 0],
13481         minWidth : 5,
13482         minHeight : 5,
13483         maxWidth : 10000,
13484         maxHeight : 10000,
13485         enabled : true,
13486         animate : false,
13487         duration : .35,
13488         dynamic : false,
13489         handles : false,
13490         multiDirectional : false,
13491         disableTrackOver : false,
13492         easing : 'easeOutStrong',
13493         widthIncrement : 0,
13494         heightIncrement : 0,
13495         pinned : false,
13496         width : null,
13497         height : null,
13498         preserveRatio : false,
13499         transparent: false,
13500         minX: 0,
13501         minY: 0,
13502         draggable: false,
13503
13504         /**
13505          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13506          */
13507         constrainTo: undefined,
13508         /**
13509          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13510          */
13511         resizeRegion: undefined,
13512
13513
13514     /**
13515      * Perform a manual resize
13516      * @param {Number} width
13517      * @param {Number} height
13518      */
13519     resizeTo : function(width, height){
13520         this.el.setSize(width, height);
13521         this.updateChildSize();
13522         this.fireEvent("resize", this, width, height, null);
13523     },
13524
13525     // private
13526     startSizing : function(e, handle){
13527         this.fireEvent("beforeresize", this, e);
13528         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13529
13530             if(!this.overlay){
13531                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13532                 this.overlay.unselectable();
13533                 this.overlay.enableDisplayMode("block");
13534                 this.overlay.on("mousemove", this.onMouseMove, this);
13535                 this.overlay.on("mouseup", this.onMouseUp, this);
13536             }
13537             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13538
13539             this.resizing = true;
13540             this.startBox = this.el.getBox();
13541             this.startPoint = e.getXY();
13542             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13543                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13544
13545             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13546             this.overlay.show();
13547
13548             if(this.constrainTo) {
13549                 var ct = Roo.get(this.constrainTo);
13550                 this.resizeRegion = ct.getRegion().adjust(
13551                     ct.getFrameWidth('t'),
13552                     ct.getFrameWidth('l'),
13553                     -ct.getFrameWidth('b'),
13554                     -ct.getFrameWidth('r')
13555                 );
13556             }
13557
13558             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13559             this.proxy.show();
13560             this.proxy.setBox(this.startBox);
13561             if(!this.dynamic){
13562                 this.proxy.setStyle('visibility', 'visible');
13563             }
13564         }
13565     },
13566
13567     // private
13568     onMouseDown : function(handle, e){
13569         if(this.enabled){
13570             e.stopEvent();
13571             this.activeHandle = handle;
13572             this.startSizing(e, handle);
13573         }
13574     },
13575
13576     // private
13577     onMouseUp : function(e){
13578         var size = this.resizeElement();
13579         this.resizing = false;
13580         this.handleOut();
13581         this.overlay.hide();
13582         this.proxy.hide();
13583         this.fireEvent("resize", this, size.width, size.height, e);
13584     },
13585
13586     // private
13587     updateChildSize : function(){
13588         if(this.resizeChild){
13589             var el = this.el;
13590             var child = this.resizeChild;
13591             var adj = this.adjustments;
13592             if(el.dom.offsetWidth){
13593                 var b = el.getSize(true);
13594                 child.setSize(b.width+adj[0], b.height+adj[1]);
13595             }
13596             // Second call here for IE
13597             // The first call enables instant resizing and
13598             // the second call corrects scroll bars if they
13599             // exist
13600             if(Roo.isIE){
13601                 setTimeout(function(){
13602                     if(el.dom.offsetWidth){
13603                         var b = el.getSize(true);
13604                         child.setSize(b.width+adj[0], b.height+adj[1]);
13605                     }
13606                 }, 10);
13607             }
13608         }
13609     },
13610
13611     // private
13612     snap : function(value, inc, min){
13613         if(!inc || !value) return value;
13614         var newValue = value;
13615         var m = value % inc;
13616         if(m > 0){
13617             if(m > (inc/2)){
13618                 newValue = value + (inc-m);
13619             }else{
13620                 newValue = value - m;
13621             }
13622         }
13623         return Math.max(min, newValue);
13624     },
13625
13626     // private
13627     resizeElement : function(){
13628         var box = this.proxy.getBox();
13629         if(this.updateBox){
13630             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13631         }else{
13632             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13633         }
13634         this.updateChildSize();
13635         if(!this.dynamic){
13636             this.proxy.hide();
13637         }
13638         return box;
13639     },
13640
13641     // private
13642     constrain : function(v, diff, m, mx){
13643         if(v - diff < m){
13644             diff = v - m;
13645         }else if(v - diff > mx){
13646             diff = mx - v;
13647         }
13648         return diff;
13649     },
13650
13651     // private
13652     onMouseMove : function(e){
13653         if(this.enabled){
13654             try{// try catch so if something goes wrong the user doesn't get hung
13655
13656             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13657                 return;
13658             }
13659
13660             //var curXY = this.startPoint;
13661             var curSize = this.curSize || this.startBox;
13662             var x = this.startBox.x, y = this.startBox.y;
13663             var ox = x, oy = y;
13664             var w = curSize.width, h = curSize.height;
13665             var ow = w, oh = h;
13666             var mw = this.minWidth, mh = this.minHeight;
13667             var mxw = this.maxWidth, mxh = this.maxHeight;
13668             var wi = this.widthIncrement;
13669             var hi = this.heightIncrement;
13670
13671             var eventXY = e.getXY();
13672             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13673             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13674
13675             var pos = this.activeHandle.position;
13676
13677             switch(pos){
13678                 case "east":
13679                     w += diffX;
13680                     w = Math.min(Math.max(mw, w), mxw);
13681                     break;
13682              
13683                 case "south":
13684                     h += diffY;
13685                     h = Math.min(Math.max(mh, h), mxh);
13686                     break;
13687                 case "southeast":
13688                     w += diffX;
13689                     h += diffY;
13690                     w = Math.min(Math.max(mw, w), mxw);
13691                     h = Math.min(Math.max(mh, h), mxh);
13692                     break;
13693                 case "north":
13694                     diffY = this.constrain(h, diffY, mh, mxh);
13695                     y += diffY;
13696                     h -= diffY;
13697                     break;
13698                 case "hdrag":
13699                     
13700                     if (wi) {
13701                         var adiffX = Math.abs(diffX);
13702                         var sub = (adiffX % wi); // how much 
13703                         if (sub > (wi/2)) { // far enough to snap
13704                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13705                         } else {
13706                             // remove difference.. 
13707                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13708                         }
13709                     }
13710                     x += diffX;
13711                     x = Math.max(this.minX, x);
13712                     break;
13713                 case "west":
13714                     diffX = this.constrain(w, diffX, mw, mxw);
13715                     x += diffX;
13716                     w -= diffX;
13717                     break;
13718                 case "northeast":
13719                     w += diffX;
13720                     w = Math.min(Math.max(mw, w), mxw);
13721                     diffY = this.constrain(h, diffY, mh, mxh);
13722                     y += diffY;
13723                     h -= diffY;
13724                     break;
13725                 case "northwest":
13726                     diffX = this.constrain(w, diffX, mw, mxw);
13727                     diffY = this.constrain(h, diffY, mh, mxh);
13728                     y += diffY;
13729                     h -= diffY;
13730                     x += diffX;
13731                     w -= diffX;
13732                     break;
13733                case "southwest":
13734                     diffX = this.constrain(w, diffX, mw, mxw);
13735                     h += diffY;
13736                     h = Math.min(Math.max(mh, h), mxh);
13737                     x += diffX;
13738                     w -= diffX;
13739                     break;
13740             }
13741
13742             var sw = this.snap(w, wi, mw);
13743             var sh = this.snap(h, hi, mh);
13744             if(sw != w || sh != h){
13745                 switch(pos){
13746                     case "northeast":
13747                         y -= sh - h;
13748                     break;
13749                     case "north":
13750                         y -= sh - h;
13751                         break;
13752                     case "southwest":
13753                         x -= sw - w;
13754                     break;
13755                     case "west":
13756                         x -= sw - w;
13757                         break;
13758                     case "northwest":
13759                         x -= sw - w;
13760                         y -= sh - h;
13761                     break;
13762                 }
13763                 w = sw;
13764                 h = sh;
13765             }
13766
13767             if(this.preserveRatio){
13768                 switch(pos){
13769                     case "southeast":
13770                     case "east":
13771                         h = oh * (w/ow);
13772                         h = Math.min(Math.max(mh, h), mxh);
13773                         w = ow * (h/oh);
13774                        break;
13775                     case "south":
13776                         w = ow * (h/oh);
13777                         w = Math.min(Math.max(mw, w), mxw);
13778                         h = oh * (w/ow);
13779                         break;
13780                     case "northeast":
13781                         w = ow * (h/oh);
13782                         w = Math.min(Math.max(mw, w), mxw);
13783                         h = oh * (w/ow);
13784                     break;
13785                     case "north":
13786                         var tw = w;
13787                         w = ow * (h/oh);
13788                         w = Math.min(Math.max(mw, w), mxw);
13789                         h = oh * (w/ow);
13790                         x += (tw - w) / 2;
13791                         break;
13792                     case "southwest":
13793                         h = oh * (w/ow);
13794                         h = Math.min(Math.max(mh, h), mxh);
13795                         var tw = w;
13796                         w = ow * (h/oh);
13797                         x += tw - w;
13798                         break;
13799                     case "west":
13800                         var th = h;
13801                         h = oh * (w/ow);
13802                         h = Math.min(Math.max(mh, h), mxh);
13803                         y += (th - h) / 2;
13804                         var tw = w;
13805                         w = ow * (h/oh);
13806                         x += tw - w;
13807                        break;
13808                     case "northwest":
13809                         var tw = w;
13810                         var th = h;
13811                         h = oh * (w/ow);
13812                         h = Math.min(Math.max(mh, h), mxh);
13813                         w = ow * (h/oh);
13814                         y += th - h;
13815                         x += tw - w;
13816                        break;
13817
13818                 }
13819             }
13820             if (pos == 'hdrag') {
13821                 w = ow;
13822             }
13823             this.proxy.setBounds(x, y, w, h);
13824             if(this.dynamic){
13825                 this.resizeElement();
13826             }
13827             }catch(e){}
13828         }
13829     },
13830
13831     // private
13832     handleOver : function(){
13833         if(this.enabled){
13834             this.el.addClass("x-resizable-over");
13835         }
13836     },
13837
13838     // private
13839     handleOut : function(){
13840         if(!this.resizing){
13841             this.el.removeClass("x-resizable-over");
13842         }
13843     },
13844
13845     /**
13846      * Returns the element this component is bound to.
13847      * @return {Roo.Element}
13848      */
13849     getEl : function(){
13850         return this.el;
13851     },
13852
13853     /**
13854      * Returns the resizeChild element (or null).
13855      * @return {Roo.Element}
13856      */
13857     getResizeChild : function(){
13858         return this.resizeChild;
13859     },
13860
13861     /**
13862      * Destroys this resizable. If the element was wrapped and
13863      * removeEl is not true then the element remains.
13864      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13865      */
13866     destroy : function(removeEl){
13867         this.proxy.remove();
13868         if(this.overlay){
13869             this.overlay.removeAllListeners();
13870             this.overlay.remove();
13871         }
13872         var ps = Roo.Resizable.positions;
13873         for(var k in ps){
13874             if(typeof ps[k] != "function" && this[ps[k]]){
13875                 var h = this[ps[k]];
13876                 h.el.removeAllListeners();
13877                 h.el.remove();
13878             }
13879         }
13880         if(removeEl){
13881             this.el.update("");
13882             this.el.remove();
13883         }
13884     }
13885 });
13886
13887 // private
13888 // hash to map config positions to true positions
13889 Roo.Resizable.positions = {
13890     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13891     hd: "hdrag"
13892 };
13893
13894 // private
13895 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13896     if(!this.tpl){
13897         // only initialize the template if resizable is used
13898         var tpl = Roo.DomHelper.createTemplate(
13899             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13900         );
13901         tpl.compile();
13902         Roo.Resizable.Handle.prototype.tpl = tpl;
13903     }
13904     this.position = pos;
13905     this.rz = rz;
13906     // show north drag fro topdra
13907     var handlepos = pos == 'hdrag' ? 'north' : pos;
13908     
13909     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13910     if (pos == 'hdrag') {
13911         this.el.setStyle('cursor', 'pointer');
13912     }
13913     this.el.unselectable();
13914     if(transparent){
13915         this.el.setOpacity(0);
13916     }
13917     this.el.on("mousedown", this.onMouseDown, this);
13918     if(!disableTrackOver){
13919         this.el.on("mouseover", this.onMouseOver, this);
13920         this.el.on("mouseout", this.onMouseOut, this);
13921     }
13922 };
13923
13924 // private
13925 Roo.Resizable.Handle.prototype = {
13926     afterResize : function(rz){
13927         // do nothing
13928     },
13929     // private
13930     onMouseDown : function(e){
13931         this.rz.onMouseDown(this, e);
13932     },
13933     // private
13934     onMouseOver : function(e){
13935         this.rz.handleOver(this, e);
13936     },
13937     // private
13938     onMouseOut : function(e){
13939         this.rz.handleOut(this, e);
13940     }
13941 };/*
13942  * Based on:
13943  * Ext JS Library 1.1.1
13944  * Copyright(c) 2006-2007, Ext JS, LLC.
13945  *
13946  * Originally Released Under LGPL - original licence link has changed is not relivant.
13947  *
13948  * Fork - LGPL
13949  * <script type="text/javascript">
13950  */
13951
13952 /**
13953  * @class Roo.Editor
13954  * @extends Roo.Component
13955  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13956  * @constructor
13957  * Create a new Editor
13958  * @param {Roo.form.Field} field The Field object (or descendant)
13959  * @param {Object} config The config object
13960  */
13961 Roo.Editor = function(field, config){
13962     Roo.Editor.superclass.constructor.call(this, config);
13963     this.field = field;
13964     this.addEvents({
13965         /**
13966              * @event beforestartedit
13967              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13968              * false from the handler of this event.
13969              * @param {Editor} this
13970              * @param {Roo.Element} boundEl The underlying element bound to this editor
13971              * @param {Mixed} value The field value being set
13972              */
13973         "beforestartedit" : true,
13974         /**
13975              * @event startedit
13976              * Fires when this editor is displayed
13977              * @param {Roo.Element} boundEl The underlying element bound to this editor
13978              * @param {Mixed} value The starting field value
13979              */
13980         "startedit" : true,
13981         /**
13982              * @event beforecomplete
13983              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13984              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13985              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13986              * event will not fire since no edit actually occurred.
13987              * @param {Editor} this
13988              * @param {Mixed} value The current field value
13989              * @param {Mixed} startValue The original field value
13990              */
13991         "beforecomplete" : true,
13992         /**
13993              * @event complete
13994              * Fires after editing is complete and any changed value has been written to the underlying field.
13995              * @param {Editor} this
13996              * @param {Mixed} value The current field value
13997              * @param {Mixed} startValue The original field value
13998              */
13999         "complete" : true,
14000         /**
14001          * @event specialkey
14002          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
14003          * {@link Roo.EventObject#getKey} to determine which key was pressed.
14004          * @param {Roo.form.Field} this
14005          * @param {Roo.EventObject} e The event object
14006          */
14007         "specialkey" : true
14008     });
14009 };
14010
14011 Roo.extend(Roo.Editor, Roo.Component, {
14012     /**
14013      * @cfg {Boolean/String} autosize
14014      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14015      * or "height" to adopt the height only (defaults to false)
14016      */
14017     /**
14018      * @cfg {Boolean} revertInvalid
14019      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14020      * validation fails (defaults to true)
14021      */
14022     /**
14023      * @cfg {Boolean} ignoreNoChange
14024      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14025      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
14026      * will never be ignored.
14027      */
14028     /**
14029      * @cfg {Boolean} hideEl
14030      * False to keep the bound element visible while the editor is displayed (defaults to true)
14031      */
14032     /**
14033      * @cfg {Mixed} value
14034      * The data value of the underlying field (defaults to "")
14035      */
14036     value : "",
14037     /**
14038      * @cfg {String} alignment
14039      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14040      */
14041     alignment: "c-c?",
14042     /**
14043      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14044      * for bottom-right shadow (defaults to "frame")
14045      */
14046     shadow : "frame",
14047     /**
14048      * @cfg {Boolean} constrain True to constrain the editor to the viewport
14049      */
14050     constrain : false,
14051     /**
14052      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14053      */
14054     completeOnEnter : false,
14055     /**
14056      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14057      */
14058     cancelOnEsc : false,
14059     /**
14060      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14061      */
14062     updateEl : false,
14063
14064     // private
14065     onRender : function(ct, position){
14066         this.el = new Roo.Layer({
14067             shadow: this.shadow,
14068             cls: "x-editor",
14069             parentEl : ct,
14070             shim : this.shim,
14071             shadowOffset:4,
14072             id: this.id,
14073             constrain: this.constrain
14074         });
14075         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14076         if(this.field.msgTarget != 'title'){
14077             this.field.msgTarget = 'qtip';
14078         }
14079         this.field.render(this.el);
14080         if(Roo.isGecko){
14081             this.field.el.dom.setAttribute('autocomplete', 'off');
14082         }
14083         this.field.on("specialkey", this.onSpecialKey, this);
14084         if(this.swallowKeys){
14085             this.field.el.swallowEvent(['keydown','keypress']);
14086         }
14087         this.field.show();
14088         this.field.on("blur", this.onBlur, this);
14089         if(this.field.grow){
14090             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14091         }
14092     },
14093
14094     onSpecialKey : function(field, e)
14095     {
14096         //Roo.log('editor onSpecialKey');
14097         if(this.completeOnEnter && e.getKey() == e.ENTER){
14098             e.stopEvent();
14099             this.completeEdit();
14100             return;
14101         }
14102         // do not fire special key otherwise it might hide close the editor...
14103         if(e.getKey() == e.ENTER){    
14104             return;
14105         }
14106         if(this.cancelOnEsc && e.getKey() == e.ESC){
14107             this.cancelEdit();
14108             return;
14109         } 
14110         this.fireEvent('specialkey', field, e);
14111     
14112     },
14113
14114     /**
14115      * Starts the editing process and shows the editor.
14116      * @param {String/HTMLElement/Element} el The element to edit
14117      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14118       * to the innerHTML of el.
14119      */
14120     startEdit : function(el, value){
14121         if(this.editing){
14122             this.completeEdit();
14123         }
14124         this.boundEl = Roo.get(el);
14125         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14126         if(!this.rendered){
14127             this.render(this.parentEl || document.body);
14128         }
14129         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14130             return;
14131         }
14132         this.startValue = v;
14133         this.field.setValue(v);
14134         if(this.autoSize){
14135             var sz = this.boundEl.getSize();
14136             switch(this.autoSize){
14137                 case "width":
14138                 this.setSize(sz.width,  "");
14139                 break;
14140                 case "height":
14141                 this.setSize("",  sz.height);
14142                 break;
14143                 default:
14144                 this.setSize(sz.width,  sz.height);
14145             }
14146         }
14147         this.el.alignTo(this.boundEl, this.alignment);
14148         this.editing = true;
14149         if(Roo.QuickTips){
14150             Roo.QuickTips.disable();
14151         }
14152         this.show();
14153     },
14154
14155     /**
14156      * Sets the height and width of this editor.
14157      * @param {Number} width The new width
14158      * @param {Number} height The new height
14159      */
14160     setSize : function(w, h){
14161         this.field.setSize(w, h);
14162         if(this.el){
14163             this.el.sync();
14164         }
14165     },
14166
14167     /**
14168      * Realigns the editor to the bound field based on the current alignment config value.
14169      */
14170     realign : function(){
14171         this.el.alignTo(this.boundEl, this.alignment);
14172     },
14173
14174     /**
14175      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14176      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14177      */
14178     completeEdit : function(remainVisible){
14179         if(!this.editing){
14180             return;
14181         }
14182         var v = this.getValue();
14183         if(this.revertInvalid !== false && !this.field.isValid()){
14184             v = this.startValue;
14185             this.cancelEdit(true);
14186         }
14187         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14188             this.editing = false;
14189             this.hide();
14190             return;
14191         }
14192         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14193             this.editing = false;
14194             if(this.updateEl && this.boundEl){
14195                 this.boundEl.update(v);
14196             }
14197             if(remainVisible !== true){
14198                 this.hide();
14199             }
14200             this.fireEvent("complete", this, v, this.startValue);
14201         }
14202     },
14203
14204     // private
14205     onShow : function(){
14206         this.el.show();
14207         if(this.hideEl !== false){
14208             this.boundEl.hide();
14209         }
14210         this.field.show();
14211         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14212             this.fixIEFocus = true;
14213             this.deferredFocus.defer(50, this);
14214         }else{
14215             this.field.focus();
14216         }
14217         this.fireEvent("startedit", this.boundEl, this.startValue);
14218     },
14219
14220     deferredFocus : function(){
14221         if(this.editing){
14222             this.field.focus();
14223         }
14224     },
14225
14226     /**
14227      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14228      * reverted to the original starting value.
14229      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14230      * cancel (defaults to false)
14231      */
14232     cancelEdit : function(remainVisible){
14233         if(this.editing){
14234             this.setValue(this.startValue);
14235             if(remainVisible !== true){
14236                 this.hide();
14237             }
14238         }
14239     },
14240
14241     // private
14242     onBlur : function(){
14243         if(this.allowBlur !== true && this.editing){
14244             this.completeEdit();
14245         }
14246     },
14247
14248     // private
14249     onHide : function(){
14250         if(this.editing){
14251             this.completeEdit();
14252             return;
14253         }
14254         this.field.blur();
14255         if(this.field.collapse){
14256             this.field.collapse();
14257         }
14258         this.el.hide();
14259         if(this.hideEl !== false){
14260             this.boundEl.show();
14261         }
14262         if(Roo.QuickTips){
14263             Roo.QuickTips.enable();
14264         }
14265     },
14266
14267     /**
14268      * Sets the data value of the editor
14269      * @param {Mixed} value Any valid value supported by the underlying field
14270      */
14271     setValue : function(v){
14272         this.field.setValue(v);
14273     },
14274
14275     /**
14276      * Gets the data value of the editor
14277      * @return {Mixed} The data value
14278      */
14279     getValue : function(){
14280         return this.field.getValue();
14281     }
14282 });/*
14283  * Based on:
14284  * Ext JS Library 1.1.1
14285  * Copyright(c) 2006-2007, Ext JS, LLC.
14286  *
14287  * Originally Released Under LGPL - original licence link has changed is not relivant.
14288  *
14289  * Fork - LGPL
14290  * <script type="text/javascript">
14291  */
14292  
14293 /**
14294  * @class Roo.BasicDialog
14295  * @extends Roo.util.Observable
14296  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14297  * <pre><code>
14298 var dlg = new Roo.BasicDialog("my-dlg", {
14299     height: 200,
14300     width: 300,
14301     minHeight: 100,
14302     minWidth: 150,
14303     modal: true,
14304     proxyDrag: true,
14305     shadow: true
14306 });
14307 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14308 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14309 dlg.addButton('Cancel', dlg.hide, dlg);
14310 dlg.show();
14311 </code></pre>
14312   <b>A Dialog should always be a direct child of the body element.</b>
14313  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14314  * @cfg {String} title Default text to display in the title bar (defaults to null)
14315  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14316  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14317  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14318  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14319  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14320  * (defaults to null with no animation)
14321  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14322  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14323  * property for valid values (defaults to 'all')
14324  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14325  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14326  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14327  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14328  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14329  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14330  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14331  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14332  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14333  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14334  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14335  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14336  * draggable = true (defaults to false)
14337  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14338  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14339  * shadow (defaults to false)
14340  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14341  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14342  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14343  * @cfg {Array} buttons Array of buttons
14344  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14345  * @constructor
14346  * Create a new BasicDialog.
14347  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14348  * @param {Object} config Configuration options
14349  */
14350 Roo.BasicDialog = function(el, config){
14351     this.el = Roo.get(el);
14352     var dh = Roo.DomHelper;
14353     if(!this.el && config && config.autoCreate){
14354         if(typeof config.autoCreate == "object"){
14355             if(!config.autoCreate.id){
14356                 config.autoCreate.id = el;
14357             }
14358             this.el = dh.append(document.body,
14359                         config.autoCreate, true);
14360         }else{
14361             this.el = dh.append(document.body,
14362                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14363         }
14364     }
14365     el = this.el;
14366     el.setDisplayed(true);
14367     el.hide = this.hideAction;
14368     this.id = el.id;
14369     el.addClass("x-dlg");
14370
14371     Roo.apply(this, config);
14372
14373     this.proxy = el.createProxy("x-dlg-proxy");
14374     this.proxy.hide = this.hideAction;
14375     this.proxy.setOpacity(.5);
14376     this.proxy.hide();
14377
14378     if(config.width){
14379         el.setWidth(config.width);
14380     }
14381     if(config.height){
14382         el.setHeight(config.height);
14383     }
14384     this.size = el.getSize();
14385     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14386         this.xy = [config.x,config.y];
14387     }else{
14388         this.xy = el.getCenterXY(true);
14389     }
14390     /** The header element @type Roo.Element */
14391     this.header = el.child("> .x-dlg-hd");
14392     /** The body element @type Roo.Element */
14393     this.body = el.child("> .x-dlg-bd");
14394     /** The footer element @type Roo.Element */
14395     this.footer = el.child("> .x-dlg-ft");
14396
14397     if(!this.header){
14398         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14399     }
14400     if(!this.body){
14401         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14402     }
14403
14404     this.header.unselectable();
14405     if(this.title){
14406         this.header.update(this.title);
14407     }
14408     // this element allows the dialog to be focused for keyboard event
14409     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14410     this.focusEl.swallowEvent("click", true);
14411
14412     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14413
14414     // wrap the body and footer for special rendering
14415     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14416     if(this.footer){
14417         this.bwrap.dom.appendChild(this.footer.dom);
14418     }
14419
14420     this.bg = this.el.createChild({
14421         tag: "div", cls:"x-dlg-bg",
14422         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14423     });
14424     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14425
14426
14427     if(this.autoScroll !== false && !this.autoTabs){
14428         this.body.setStyle("overflow", "auto");
14429     }
14430
14431     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14432
14433     if(this.closable !== false){
14434         this.el.addClass("x-dlg-closable");
14435         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14436         this.close.on("click", this.closeClick, this);
14437         this.close.addClassOnOver("x-dlg-close-over");
14438     }
14439     if(this.collapsible !== false){
14440         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14441         this.collapseBtn.on("click", this.collapseClick, this);
14442         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14443         this.header.on("dblclick", this.collapseClick, this);
14444     }
14445     if(this.resizable !== false){
14446         this.el.addClass("x-dlg-resizable");
14447         this.resizer = new Roo.Resizable(el, {
14448             minWidth: this.minWidth || 80,
14449             minHeight:this.minHeight || 80,
14450             handles: this.resizeHandles || "all",
14451             pinned: true
14452         });
14453         this.resizer.on("beforeresize", this.beforeResize, this);
14454         this.resizer.on("resize", this.onResize, this);
14455     }
14456     if(this.draggable !== false){
14457         el.addClass("x-dlg-draggable");
14458         if (!this.proxyDrag) {
14459             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14460         }
14461         else {
14462             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14463         }
14464         dd.setHandleElId(this.header.id);
14465         dd.endDrag = this.endMove.createDelegate(this);
14466         dd.startDrag = this.startMove.createDelegate(this);
14467         dd.onDrag = this.onDrag.createDelegate(this);
14468         dd.scroll = false;
14469         this.dd = dd;
14470     }
14471     if(this.modal){
14472         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14473         this.mask.enableDisplayMode("block");
14474         this.mask.hide();
14475         this.el.addClass("x-dlg-modal");
14476     }
14477     if(this.shadow){
14478         this.shadow = new Roo.Shadow({
14479             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14480             offset : this.shadowOffset
14481         });
14482     }else{
14483         this.shadowOffset = 0;
14484     }
14485     if(Roo.useShims && this.shim !== false){
14486         this.shim = this.el.createShim();
14487         this.shim.hide = this.hideAction;
14488         this.shim.hide();
14489     }else{
14490         this.shim = false;
14491     }
14492     if(this.autoTabs){
14493         this.initTabs();
14494     }
14495     if (this.buttons) { 
14496         var bts= this.buttons;
14497         this.buttons = [];
14498         Roo.each(bts, function(b) {
14499             this.addButton(b);
14500         }, this);
14501     }
14502     
14503     
14504     this.addEvents({
14505         /**
14506          * @event keydown
14507          * Fires when a key is pressed
14508          * @param {Roo.BasicDialog} this
14509          * @param {Roo.EventObject} e
14510          */
14511         "keydown" : true,
14512         /**
14513          * @event move
14514          * Fires when this dialog is moved by the user.
14515          * @param {Roo.BasicDialog} this
14516          * @param {Number} x The new page X
14517          * @param {Number} y The new page Y
14518          */
14519         "move" : true,
14520         /**
14521          * @event resize
14522          * Fires when this dialog is resized by the user.
14523          * @param {Roo.BasicDialog} this
14524          * @param {Number} width The new width
14525          * @param {Number} height The new height
14526          */
14527         "resize" : true,
14528         /**
14529          * @event beforehide
14530          * Fires before this dialog is hidden.
14531          * @param {Roo.BasicDialog} this
14532          */
14533         "beforehide" : true,
14534         /**
14535          * @event hide
14536          * Fires when this dialog is hidden.
14537          * @param {Roo.BasicDialog} this
14538          */
14539         "hide" : true,
14540         /**
14541          * @event beforeshow
14542          * Fires before this dialog is shown.
14543          * @param {Roo.BasicDialog} this
14544          */
14545         "beforeshow" : true,
14546         /**
14547          * @event show
14548          * Fires when this dialog is shown.
14549          * @param {Roo.BasicDialog} this
14550          */
14551         "show" : true
14552     });
14553     el.on("keydown", this.onKeyDown, this);
14554     el.on("mousedown", this.toFront, this);
14555     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14556     this.el.hide();
14557     Roo.DialogManager.register(this);
14558     Roo.BasicDialog.superclass.constructor.call(this);
14559 };
14560
14561 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14562     shadowOffset: Roo.isIE ? 6 : 5,
14563     minHeight: 80,
14564     minWidth: 200,
14565     minButtonWidth: 75,
14566     defaultButton: null,
14567     buttonAlign: "right",
14568     tabTag: 'div',
14569     firstShow: true,
14570
14571     /**
14572      * Sets the dialog title text
14573      * @param {String} text The title text to display
14574      * @return {Roo.BasicDialog} this
14575      */
14576     setTitle : function(text){
14577         this.header.update(text);
14578         return this;
14579     },
14580
14581     // private
14582     closeClick : function(){
14583         this.hide();
14584     },
14585
14586     // private
14587     collapseClick : function(){
14588         this[this.collapsed ? "expand" : "collapse"]();
14589     },
14590
14591     /**
14592      * Collapses the dialog to its minimized state (only the title bar is visible).
14593      * Equivalent to the user clicking the collapse dialog button.
14594      */
14595     collapse : function(){
14596         if(!this.collapsed){
14597             this.collapsed = true;
14598             this.el.addClass("x-dlg-collapsed");
14599             this.restoreHeight = this.el.getHeight();
14600             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14601         }
14602     },
14603
14604     /**
14605      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14606      * clicking the expand dialog button.
14607      */
14608     expand : function(){
14609         if(this.collapsed){
14610             this.collapsed = false;
14611             this.el.removeClass("x-dlg-collapsed");
14612             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14613         }
14614     },
14615
14616     /**
14617      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14618      * @return {Roo.TabPanel} The tabs component
14619      */
14620     initTabs : function(){
14621         var tabs = this.getTabs();
14622         while(tabs.getTab(0)){
14623             tabs.removeTab(0);
14624         }
14625         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14626             var dom = el.dom;
14627             tabs.addTab(Roo.id(dom), dom.title);
14628             dom.title = "";
14629         });
14630         tabs.activate(0);
14631         return tabs;
14632     },
14633
14634     // private
14635     beforeResize : function(){
14636         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14637     },
14638
14639     // private
14640     onResize : function(){
14641         this.refreshSize();
14642         this.syncBodyHeight();
14643         this.adjustAssets();
14644         this.focus();
14645         this.fireEvent("resize", this, this.size.width, this.size.height);
14646     },
14647
14648     // private
14649     onKeyDown : function(e){
14650         if(this.isVisible()){
14651             this.fireEvent("keydown", this, e);
14652         }
14653     },
14654
14655     /**
14656      * Resizes the dialog.
14657      * @param {Number} width
14658      * @param {Number} height
14659      * @return {Roo.BasicDialog} this
14660      */
14661     resizeTo : function(width, height){
14662         this.el.setSize(width, height);
14663         this.size = {width: width, height: height};
14664         this.syncBodyHeight();
14665         if(this.fixedcenter){
14666             this.center();
14667         }
14668         if(this.isVisible()){
14669             this.constrainXY();
14670             this.adjustAssets();
14671         }
14672         this.fireEvent("resize", this, width, height);
14673         return this;
14674     },
14675
14676
14677     /**
14678      * Resizes the dialog to fit the specified content size.
14679      * @param {Number} width
14680      * @param {Number} height
14681      * @return {Roo.BasicDialog} this
14682      */
14683     setContentSize : function(w, h){
14684         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14685         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14686         //if(!this.el.isBorderBox()){
14687             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14688             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14689         //}
14690         if(this.tabs){
14691             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14692             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14693         }
14694         this.resizeTo(w, h);
14695         return this;
14696     },
14697
14698     /**
14699      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14700      * executed in response to a particular key being pressed while the dialog is active.
14701      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14702      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14703      * @param {Function} fn The function to call
14704      * @param {Object} scope (optional) The scope of the function
14705      * @return {Roo.BasicDialog} this
14706      */
14707     addKeyListener : function(key, fn, scope){
14708         var keyCode, shift, ctrl, alt;
14709         if(typeof key == "object" && !(key instanceof Array)){
14710             keyCode = key["key"];
14711             shift = key["shift"];
14712             ctrl = key["ctrl"];
14713             alt = key["alt"];
14714         }else{
14715             keyCode = key;
14716         }
14717         var handler = function(dlg, e){
14718             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14719                 var k = e.getKey();
14720                 if(keyCode instanceof Array){
14721                     for(var i = 0, len = keyCode.length; i < len; i++){
14722                         if(keyCode[i] == k){
14723                           fn.call(scope || window, dlg, k, e);
14724                           return;
14725                         }
14726                     }
14727                 }else{
14728                     if(k == keyCode){
14729                         fn.call(scope || window, dlg, k, e);
14730                     }
14731                 }
14732             }
14733         };
14734         this.on("keydown", handler);
14735         return this;
14736     },
14737
14738     /**
14739      * Returns the TabPanel component (creates it if it doesn't exist).
14740      * Note: If you wish to simply check for the existence of tabs without creating them,
14741      * check for a null 'tabs' property.
14742      * @return {Roo.TabPanel} The tabs component
14743      */
14744     getTabs : function(){
14745         if(!this.tabs){
14746             this.el.addClass("x-dlg-auto-tabs");
14747             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14748             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14749         }
14750         return this.tabs;
14751     },
14752
14753     /**
14754      * Adds a button to the footer section of the dialog.
14755      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14756      * object or a valid Roo.DomHelper element config
14757      * @param {Function} handler The function called when the button is clicked
14758      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14759      * @return {Roo.Button} The new button
14760      */
14761     addButton : function(config, handler, scope){
14762         var dh = Roo.DomHelper;
14763         if(!this.footer){
14764             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14765         }
14766         if(!this.btnContainer){
14767             var tb = this.footer.createChild({
14768
14769                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14770                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14771             }, null, true);
14772             this.btnContainer = tb.firstChild.firstChild.firstChild;
14773         }
14774         var bconfig = {
14775             handler: handler,
14776             scope: scope,
14777             minWidth: this.minButtonWidth,
14778             hideParent:true
14779         };
14780         if(typeof config == "string"){
14781             bconfig.text = config;
14782         }else{
14783             if(config.tag){
14784                 bconfig.dhconfig = config;
14785             }else{
14786                 Roo.apply(bconfig, config);
14787             }
14788         }
14789         var fc = false;
14790         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14791             bconfig.position = Math.max(0, bconfig.position);
14792             fc = this.btnContainer.childNodes[bconfig.position];
14793         }
14794          
14795         var btn = new Roo.Button(
14796             fc ? 
14797                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14798                 : this.btnContainer.appendChild(document.createElement("td")),
14799             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14800             bconfig
14801         );
14802         this.syncBodyHeight();
14803         if(!this.buttons){
14804             /**
14805              * Array of all the buttons that have been added to this dialog via addButton
14806              * @type Array
14807              */
14808             this.buttons = [];
14809         }
14810         this.buttons.push(btn);
14811         return btn;
14812     },
14813
14814     /**
14815      * Sets the default button to be focused when the dialog is displayed.
14816      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14817      * @return {Roo.BasicDialog} this
14818      */
14819     setDefaultButton : function(btn){
14820         this.defaultButton = btn;
14821         return this;
14822     },
14823
14824     // private
14825     getHeaderFooterHeight : function(safe){
14826         var height = 0;
14827         if(this.header){
14828            height += this.header.getHeight();
14829         }
14830         if(this.footer){
14831            var fm = this.footer.getMargins();
14832             height += (this.footer.getHeight()+fm.top+fm.bottom);
14833         }
14834         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14835         height += this.centerBg.getPadding("tb");
14836         return height;
14837     },
14838
14839     // private
14840     syncBodyHeight : function(){
14841         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14842         var height = this.size.height - this.getHeaderFooterHeight(false);
14843         bd.setHeight(height-bd.getMargins("tb"));
14844         var hh = this.header.getHeight();
14845         var h = this.size.height-hh;
14846         cb.setHeight(h);
14847         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14848         bw.setHeight(h-cb.getPadding("tb"));
14849         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14850         bd.setWidth(bw.getWidth(true));
14851         if(this.tabs){
14852             this.tabs.syncHeight();
14853             if(Roo.isIE){
14854                 this.tabs.el.repaint();
14855             }
14856         }
14857     },
14858
14859     /**
14860      * Restores the previous state of the dialog if Roo.state is configured.
14861      * @return {Roo.BasicDialog} this
14862      */
14863     restoreState : function(){
14864         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14865         if(box && box.width){
14866             this.xy = [box.x, box.y];
14867             this.resizeTo(box.width, box.height);
14868         }
14869         return this;
14870     },
14871
14872     // private
14873     beforeShow : function(){
14874         this.expand();
14875         if(this.fixedcenter){
14876             this.xy = this.el.getCenterXY(true);
14877         }
14878         if(this.modal){
14879             Roo.get(document.body).addClass("x-body-masked");
14880             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14881             this.mask.show();
14882         }
14883         this.constrainXY();
14884     },
14885
14886     // private
14887     animShow : function(){
14888         var b = Roo.get(this.animateTarget).getBox();
14889         this.proxy.setSize(b.width, b.height);
14890         this.proxy.setLocation(b.x, b.y);
14891         this.proxy.show();
14892         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14893                     true, .35, this.showEl.createDelegate(this));
14894     },
14895
14896     /**
14897      * Shows the dialog.
14898      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14899      * @return {Roo.BasicDialog} this
14900      */
14901     show : function(animateTarget){
14902         if (this.fireEvent("beforeshow", this) === false){
14903             return;
14904         }
14905         if(this.syncHeightBeforeShow){
14906             this.syncBodyHeight();
14907         }else if(this.firstShow){
14908             this.firstShow = false;
14909             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14910         }
14911         this.animateTarget = animateTarget || this.animateTarget;
14912         if(!this.el.isVisible()){
14913             this.beforeShow();
14914             if(this.animateTarget && Roo.get(this.animateTarget)){
14915                 this.animShow();
14916             }else{
14917                 this.showEl();
14918             }
14919         }
14920         return this;
14921     },
14922
14923     // private
14924     showEl : function(){
14925         this.proxy.hide();
14926         this.el.setXY(this.xy);
14927         this.el.show();
14928         this.adjustAssets(true);
14929         this.toFront();
14930         this.focus();
14931         // IE peekaboo bug - fix found by Dave Fenwick
14932         if(Roo.isIE){
14933             this.el.repaint();
14934         }
14935         this.fireEvent("show", this);
14936     },
14937
14938     /**
14939      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14940      * dialog itself will receive focus.
14941      */
14942     focus : function(){
14943         if(this.defaultButton){
14944             this.defaultButton.focus();
14945         }else{
14946             this.focusEl.focus();
14947         }
14948     },
14949
14950     // private
14951     constrainXY : function(){
14952         if(this.constraintoviewport !== false){
14953             if(!this.viewSize){
14954                 if(this.container){
14955                     var s = this.container.getSize();
14956                     this.viewSize = [s.width, s.height];
14957                 }else{
14958                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14959                 }
14960             }
14961             var s = Roo.get(this.container||document).getScroll();
14962
14963             var x = this.xy[0], y = this.xy[1];
14964             var w = this.size.width, h = this.size.height;
14965             var vw = this.viewSize[0], vh = this.viewSize[1];
14966             // only move it if it needs it
14967             var moved = false;
14968             // first validate right/bottom
14969             if(x + w > vw+s.left){
14970                 x = vw - w;
14971                 moved = true;
14972             }
14973             if(y + h > vh+s.top){
14974                 y = vh - h;
14975                 moved = true;
14976             }
14977             // then make sure top/left isn't negative
14978             if(x < s.left){
14979                 x = s.left;
14980                 moved = true;
14981             }
14982             if(y < s.top){
14983                 y = s.top;
14984                 moved = true;
14985             }
14986             if(moved){
14987                 // cache xy
14988                 this.xy = [x, y];
14989                 if(this.isVisible()){
14990                     this.el.setLocation(x, y);
14991                     this.adjustAssets();
14992                 }
14993             }
14994         }
14995     },
14996
14997     // private
14998     onDrag : function(){
14999         if(!this.proxyDrag){
15000             this.xy = this.el.getXY();
15001             this.adjustAssets();
15002         }
15003     },
15004
15005     // private
15006     adjustAssets : function(doShow){
15007         var x = this.xy[0], y = this.xy[1];
15008         var w = this.size.width, h = this.size.height;
15009         if(doShow === true){
15010             if(this.shadow){
15011                 this.shadow.show(this.el);
15012             }
15013             if(this.shim){
15014                 this.shim.show();
15015             }
15016         }
15017         if(this.shadow && this.shadow.isVisible()){
15018             this.shadow.show(this.el);
15019         }
15020         if(this.shim && this.shim.isVisible()){
15021             this.shim.setBounds(x, y, w, h);
15022         }
15023     },
15024
15025     // private
15026     adjustViewport : function(w, h){
15027         if(!w || !h){
15028             w = Roo.lib.Dom.getViewWidth();
15029             h = Roo.lib.Dom.getViewHeight();
15030         }
15031         // cache the size
15032         this.viewSize = [w, h];
15033         if(this.modal && this.mask.isVisible()){
15034             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15035             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15036         }
15037         if(this.isVisible()){
15038             this.constrainXY();
15039         }
15040     },
15041
15042     /**
15043      * Destroys this dialog and all its supporting elements (including any tabs, shim,
15044      * shadow, proxy, mask, etc.)  Also removes all event listeners.
15045      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15046      */
15047     destroy : function(removeEl){
15048         if(this.isVisible()){
15049             this.animateTarget = null;
15050             this.hide();
15051         }
15052         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15053         if(this.tabs){
15054             this.tabs.destroy(removeEl);
15055         }
15056         Roo.destroy(
15057              this.shim,
15058              this.proxy,
15059              this.resizer,
15060              this.close,
15061              this.mask
15062         );
15063         if(this.dd){
15064             this.dd.unreg();
15065         }
15066         if(this.buttons){
15067            for(var i = 0, len = this.buttons.length; i < len; i++){
15068                this.buttons[i].destroy();
15069            }
15070         }
15071         this.el.removeAllListeners();
15072         if(removeEl === true){
15073             this.el.update("");
15074             this.el.remove();
15075         }
15076         Roo.DialogManager.unregister(this);
15077     },
15078
15079     // private
15080     startMove : function(){
15081         if(this.proxyDrag){
15082             this.proxy.show();
15083         }
15084         if(this.constraintoviewport !== false){
15085             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15086         }
15087     },
15088
15089     // private
15090     endMove : function(){
15091         if(!this.proxyDrag){
15092             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15093         }else{
15094             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15095             this.proxy.hide();
15096         }
15097         this.refreshSize();
15098         this.adjustAssets();
15099         this.focus();
15100         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15101     },
15102
15103     /**
15104      * Brings this dialog to the front of any other visible dialogs
15105      * @return {Roo.BasicDialog} this
15106      */
15107     toFront : function(){
15108         Roo.DialogManager.bringToFront(this);
15109         return this;
15110     },
15111
15112     /**
15113      * Sends this dialog to the back (under) of any other visible dialogs
15114      * @return {Roo.BasicDialog} this
15115      */
15116     toBack : function(){
15117         Roo.DialogManager.sendToBack(this);
15118         return this;
15119     },
15120
15121     /**
15122      * Centers this dialog in the viewport
15123      * @return {Roo.BasicDialog} this
15124      */
15125     center : function(){
15126         var xy = this.el.getCenterXY(true);
15127         this.moveTo(xy[0], xy[1]);
15128         return this;
15129     },
15130
15131     /**
15132      * Moves the dialog's top-left corner to the specified point
15133      * @param {Number} x
15134      * @param {Number} y
15135      * @return {Roo.BasicDialog} this
15136      */
15137     moveTo : function(x, y){
15138         this.xy = [x,y];
15139         if(this.isVisible()){
15140             this.el.setXY(this.xy);
15141             this.adjustAssets();
15142         }
15143         return this;
15144     },
15145
15146     /**
15147      * Aligns the dialog to the specified element
15148      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15149      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15150      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15151      * @return {Roo.BasicDialog} this
15152      */
15153     alignTo : function(element, position, offsets){
15154         this.xy = this.el.getAlignToXY(element, position, offsets);
15155         if(this.isVisible()){
15156             this.el.setXY(this.xy);
15157             this.adjustAssets();
15158         }
15159         return this;
15160     },
15161
15162     /**
15163      * Anchors an element to another element and realigns it when the window is resized.
15164      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15165      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15166      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15167      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15168      * is a number, it is used as the buffer delay (defaults to 50ms).
15169      * @return {Roo.BasicDialog} this
15170      */
15171     anchorTo : function(el, alignment, offsets, monitorScroll){
15172         var action = function(){
15173             this.alignTo(el, alignment, offsets);
15174         };
15175         Roo.EventManager.onWindowResize(action, this);
15176         var tm = typeof monitorScroll;
15177         if(tm != 'undefined'){
15178             Roo.EventManager.on(window, 'scroll', action, this,
15179                 {buffer: tm == 'number' ? monitorScroll : 50});
15180         }
15181         action.call(this);
15182         return this;
15183     },
15184
15185     /**
15186      * Returns true if the dialog is visible
15187      * @return {Boolean}
15188      */
15189     isVisible : function(){
15190         return this.el.isVisible();
15191     },
15192
15193     // private
15194     animHide : function(callback){
15195         var b = Roo.get(this.animateTarget).getBox();
15196         this.proxy.show();
15197         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15198         this.el.hide();
15199         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15200                     this.hideEl.createDelegate(this, [callback]));
15201     },
15202
15203     /**
15204      * Hides the dialog.
15205      * @param {Function} callback (optional) Function to call when the dialog is hidden
15206      * @return {Roo.BasicDialog} this
15207      */
15208     hide : function(callback){
15209         if (this.fireEvent("beforehide", this) === false){
15210             return;
15211         }
15212         if(this.shadow){
15213             this.shadow.hide();
15214         }
15215         if(this.shim) {
15216           this.shim.hide();
15217         }
15218         // sometimes animateTarget seems to get set.. causing problems...
15219         // this just double checks..
15220         if(this.animateTarget && Roo.get(this.animateTarget)) {
15221            this.animHide(callback);
15222         }else{
15223             this.el.hide();
15224             this.hideEl(callback);
15225         }
15226         return this;
15227     },
15228
15229     // private
15230     hideEl : function(callback){
15231         this.proxy.hide();
15232         if(this.modal){
15233             this.mask.hide();
15234             Roo.get(document.body).removeClass("x-body-masked");
15235         }
15236         this.fireEvent("hide", this);
15237         if(typeof callback == "function"){
15238             callback();
15239         }
15240     },
15241
15242     // private
15243     hideAction : function(){
15244         this.setLeft("-10000px");
15245         this.setTop("-10000px");
15246         this.setStyle("visibility", "hidden");
15247     },
15248
15249     // private
15250     refreshSize : function(){
15251         this.size = this.el.getSize();
15252         this.xy = this.el.getXY();
15253         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15254     },
15255
15256     // private
15257     // z-index is managed by the DialogManager and may be overwritten at any time
15258     setZIndex : function(index){
15259         if(this.modal){
15260             this.mask.setStyle("z-index", index);
15261         }
15262         if(this.shim){
15263             this.shim.setStyle("z-index", ++index);
15264         }
15265         if(this.shadow){
15266             this.shadow.setZIndex(++index);
15267         }
15268         this.el.setStyle("z-index", ++index);
15269         if(this.proxy){
15270             this.proxy.setStyle("z-index", ++index);
15271         }
15272         if(this.resizer){
15273             this.resizer.proxy.setStyle("z-index", ++index);
15274         }
15275
15276         this.lastZIndex = index;
15277     },
15278
15279     /**
15280      * Returns the element for this dialog
15281      * @return {Roo.Element} The underlying dialog Element
15282      */
15283     getEl : function(){
15284         return this.el;
15285     }
15286 });
15287
15288 /**
15289  * @class Roo.DialogManager
15290  * Provides global access to BasicDialogs that have been created and
15291  * support for z-indexing (layering) multiple open dialogs.
15292  */
15293 Roo.DialogManager = function(){
15294     var list = {};
15295     var accessList = [];
15296     var front = null;
15297
15298     // private
15299     var sortDialogs = function(d1, d2){
15300         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15301     };
15302
15303     // private
15304     var orderDialogs = function(){
15305         accessList.sort(sortDialogs);
15306         var seed = Roo.DialogManager.zseed;
15307         for(var i = 0, len = accessList.length; i < len; i++){
15308             var dlg = accessList[i];
15309             if(dlg){
15310                 dlg.setZIndex(seed + (i*10));
15311             }
15312         }
15313     };
15314
15315     return {
15316         /**
15317          * The starting z-index for BasicDialogs (defaults to 9000)
15318          * @type Number The z-index value
15319          */
15320         zseed : 9000,
15321
15322         // private
15323         register : function(dlg){
15324             list[dlg.id] = dlg;
15325             accessList.push(dlg);
15326         },
15327
15328         // private
15329         unregister : function(dlg){
15330             delete list[dlg.id];
15331             var i=0;
15332             var len=0;
15333             if(!accessList.indexOf){
15334                 for(  i = 0, len = accessList.length; i < len; i++){
15335                     if(accessList[i] == dlg){
15336                         accessList.splice(i, 1);
15337                         return;
15338                     }
15339                 }
15340             }else{
15341                  i = accessList.indexOf(dlg);
15342                 if(i != -1){
15343                     accessList.splice(i, 1);
15344                 }
15345             }
15346         },
15347
15348         /**
15349          * Gets a registered dialog by id
15350          * @param {String/Object} id The id of the dialog or a dialog
15351          * @return {Roo.BasicDialog} this
15352          */
15353         get : function(id){
15354             return typeof id == "object" ? id : list[id];
15355         },
15356
15357         /**
15358          * Brings the specified dialog to the front
15359          * @param {String/Object} dlg The id of the dialog or a dialog
15360          * @return {Roo.BasicDialog} this
15361          */
15362         bringToFront : function(dlg){
15363             dlg = this.get(dlg);
15364             if(dlg != front){
15365                 front = dlg;
15366                 dlg._lastAccess = new Date().getTime();
15367                 orderDialogs();
15368             }
15369             return dlg;
15370         },
15371
15372         /**
15373          * Sends the specified dialog to the back
15374          * @param {String/Object} dlg The id of the dialog or a dialog
15375          * @return {Roo.BasicDialog} this
15376          */
15377         sendToBack : function(dlg){
15378             dlg = this.get(dlg);
15379             dlg._lastAccess = -(new Date().getTime());
15380             orderDialogs();
15381             return dlg;
15382         },
15383
15384         /**
15385          * Hides all dialogs
15386          */
15387         hideAll : function(){
15388             for(var id in list){
15389                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15390                     list[id].hide();
15391                 }
15392             }
15393         }
15394     };
15395 }();
15396
15397 /**
15398  * @class Roo.LayoutDialog
15399  * @extends Roo.BasicDialog
15400  * Dialog which provides adjustments for working with a layout in a Dialog.
15401  * Add your necessary layout config options to the dialog's config.<br>
15402  * Example usage (including a nested layout):
15403  * <pre><code>
15404 if(!dialog){
15405     dialog = new Roo.LayoutDialog("download-dlg", {
15406         modal: true,
15407         width:600,
15408         height:450,
15409         shadow:true,
15410         minWidth:500,
15411         minHeight:350,
15412         autoTabs:true,
15413         proxyDrag:true,
15414         // layout config merges with the dialog config
15415         center:{
15416             tabPosition: "top",
15417             alwaysShowTabs: true
15418         }
15419     });
15420     dialog.addKeyListener(27, dialog.hide, dialog);
15421     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15422     dialog.addButton("Build It!", this.getDownload, this);
15423
15424     // we can even add nested layouts
15425     var innerLayout = new Roo.BorderLayout("dl-inner", {
15426         east: {
15427             initialSize: 200,
15428             autoScroll:true,
15429             split:true
15430         },
15431         center: {
15432             autoScroll:true
15433         }
15434     });
15435     innerLayout.beginUpdate();
15436     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15437     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15438     innerLayout.endUpdate(true);
15439
15440     var layout = dialog.getLayout();
15441     layout.beginUpdate();
15442     layout.add("center", new Roo.ContentPanel("standard-panel",
15443                         {title: "Download the Source", fitToFrame:true}));
15444     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15445                {title: "Build your own roo.js"}));
15446     layout.getRegion("center").showPanel(sp);
15447     layout.endUpdate();
15448 }
15449 </code></pre>
15450     * @constructor
15451     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15452     * @param {Object} config configuration options
15453   */
15454 Roo.LayoutDialog = function(el, cfg){
15455     
15456     var config=  cfg;
15457     if (typeof(cfg) == 'undefined') {
15458         config = Roo.apply({}, el);
15459         // not sure why we use documentElement here.. - it should always be body.
15460         // IE7 borks horribly if we use documentElement.
15461         // webkit also does not like documentElement - it creates a body element...
15462         el = Roo.get( document.body || document.documentElement ).createChild();
15463         //config.autoCreate = true;
15464     }
15465     
15466     
15467     config.autoTabs = false;
15468     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15469     this.body.setStyle({overflow:"hidden", position:"relative"});
15470     this.layout = new Roo.BorderLayout(this.body.dom, config);
15471     this.layout.monitorWindowResize = false;
15472     this.el.addClass("x-dlg-auto-layout");
15473     // fix case when center region overwrites center function
15474     this.center = Roo.BasicDialog.prototype.center;
15475     this.on("show", this.layout.layout, this.layout, true);
15476     if (config.items) {
15477         var xitems = config.items;
15478         delete config.items;
15479         Roo.each(xitems, this.addxtype, this);
15480     }
15481     
15482     
15483 };
15484 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15485     /**
15486      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15487      * @deprecated
15488      */
15489     endUpdate : function(){
15490         this.layout.endUpdate();
15491     },
15492
15493     /**
15494      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15495      *  @deprecated
15496      */
15497     beginUpdate : function(){
15498         this.layout.beginUpdate();
15499     },
15500
15501     /**
15502      * Get the BorderLayout for this dialog
15503      * @return {Roo.BorderLayout}
15504      */
15505     getLayout : function(){
15506         return this.layout;
15507     },
15508
15509     showEl : function(){
15510         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15511         if(Roo.isIE7){
15512             this.layout.layout();
15513         }
15514     },
15515
15516     // private
15517     // Use the syncHeightBeforeShow config option to control this automatically
15518     syncBodyHeight : function(){
15519         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15520         if(this.layout){this.layout.layout();}
15521     },
15522     
15523       /**
15524      * Add an xtype element (actually adds to the layout.)
15525      * @return {Object} xdata xtype object data.
15526      */
15527     
15528     addxtype : function(c) {
15529         return this.layout.addxtype(c);
15530     }
15531 });/*
15532  * Based on:
15533  * Ext JS Library 1.1.1
15534  * Copyright(c) 2006-2007, Ext JS, LLC.
15535  *
15536  * Originally Released Under LGPL - original licence link has changed is not relivant.
15537  *
15538  * Fork - LGPL
15539  * <script type="text/javascript">
15540  */
15541  
15542 /**
15543  * @class Roo.MessageBox
15544  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15545  * Example usage:
15546  *<pre><code>
15547 // Basic alert:
15548 Roo.Msg.alert('Status', 'Changes saved successfully.');
15549
15550 // Prompt for user data:
15551 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15552     if (btn == 'ok'){
15553         // process text value...
15554     }
15555 });
15556
15557 // Show a dialog using config options:
15558 Roo.Msg.show({
15559    title:'Save Changes?',
15560    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15561    buttons: Roo.Msg.YESNOCANCEL,
15562    fn: processResult,
15563    animEl: 'elId'
15564 });
15565 </code></pre>
15566  * @singleton
15567  */
15568 Roo.MessageBox = function(){
15569     var dlg, opt, mask, waitTimer;
15570     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15571     var buttons, activeTextEl, bwidth;
15572
15573     // private
15574     var handleButton = function(button){
15575         dlg.hide();
15576         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15577     };
15578
15579     // private
15580     var handleHide = function(){
15581         if(opt && opt.cls){
15582             dlg.el.removeClass(opt.cls);
15583         }
15584         if(waitTimer){
15585             Roo.TaskMgr.stop(waitTimer);
15586             waitTimer = null;
15587         }
15588     };
15589
15590     // private
15591     var updateButtons = function(b){
15592         var width = 0;
15593         if(!b){
15594             buttons["ok"].hide();
15595             buttons["cancel"].hide();
15596             buttons["yes"].hide();
15597             buttons["no"].hide();
15598             dlg.footer.dom.style.display = 'none';
15599             return width;
15600         }
15601         dlg.footer.dom.style.display = '';
15602         for(var k in buttons){
15603             if(typeof buttons[k] != "function"){
15604                 if(b[k]){
15605                     buttons[k].show();
15606                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15607                     width += buttons[k].el.getWidth()+15;
15608                 }else{
15609                     buttons[k].hide();
15610                 }
15611             }
15612         }
15613         return width;
15614     };
15615
15616     // private
15617     var handleEsc = function(d, k, e){
15618         if(opt && opt.closable !== false){
15619             dlg.hide();
15620         }
15621         if(e){
15622             e.stopEvent();
15623         }
15624     };
15625
15626     return {
15627         /**
15628          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15629          * @return {Roo.BasicDialog} The BasicDialog element
15630          */
15631         getDialog : function(){
15632            if(!dlg){
15633                 dlg = new Roo.BasicDialog("x-msg-box", {
15634                     autoCreate : true,
15635                     shadow: true,
15636                     draggable: true,
15637                     resizable:false,
15638                     constraintoviewport:false,
15639                     fixedcenter:true,
15640                     collapsible : false,
15641                     shim:true,
15642                     modal: true,
15643                     width:400, height:100,
15644                     buttonAlign:"center",
15645                     closeClick : function(){
15646                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15647                             handleButton("no");
15648                         }else{
15649                             handleButton("cancel");
15650                         }
15651                     }
15652                 });
15653                 dlg.on("hide", handleHide);
15654                 mask = dlg.mask;
15655                 dlg.addKeyListener(27, handleEsc);
15656                 buttons = {};
15657                 var bt = this.buttonText;
15658                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15659                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15660                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15661                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15662                 bodyEl = dlg.body.createChild({
15663
15664                     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>'
15665                 });
15666                 msgEl = bodyEl.dom.firstChild;
15667                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15668                 textboxEl.enableDisplayMode();
15669                 textboxEl.addKeyListener([10,13], function(){
15670                     if(dlg.isVisible() && opt && opt.buttons){
15671                         if(opt.buttons.ok){
15672                             handleButton("ok");
15673                         }else if(opt.buttons.yes){
15674                             handleButton("yes");
15675                         }
15676                     }
15677                 });
15678                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15679                 textareaEl.enableDisplayMode();
15680                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15681                 progressEl.enableDisplayMode();
15682                 var pf = progressEl.dom.firstChild;
15683                 if (pf) {
15684                     pp = Roo.get(pf.firstChild);
15685                     pp.setHeight(pf.offsetHeight);
15686                 }
15687                 
15688             }
15689             return dlg;
15690         },
15691
15692         /**
15693          * Updates the message box body text
15694          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15695          * the XHTML-compliant non-breaking space character '&amp;#160;')
15696          * @return {Roo.MessageBox} This message box
15697          */
15698         updateText : function(text){
15699             if(!dlg.isVisible() && !opt.width){
15700                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15701             }
15702             msgEl.innerHTML = text || '&#160;';
15703       
15704             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15705             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15706             var w = Math.max(
15707                     Math.min(opt.width || cw , this.maxWidth), 
15708                     Math.max(opt.minWidth || this.minWidth, bwidth)
15709             );
15710             if(opt.prompt){
15711                 activeTextEl.setWidth(w);
15712             }
15713             if(dlg.isVisible()){
15714                 dlg.fixedcenter = false;
15715             }
15716             // to big, make it scroll. = But as usual stupid IE does not support
15717             // !important..
15718             
15719             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15720                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15721                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15722             } else {
15723                 bodyEl.dom.style.height = '';
15724                 bodyEl.dom.style.overflowY = '';
15725             }
15726             if (cw > w) {
15727                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15728             } else {
15729                 bodyEl.dom.style.overflowX = '';
15730             }
15731             
15732             dlg.setContentSize(w, bodyEl.getHeight());
15733             if(dlg.isVisible()){
15734                 dlg.fixedcenter = true;
15735             }
15736             return this;
15737         },
15738
15739         /**
15740          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15741          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15742          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15743          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15744          * @return {Roo.MessageBox} This message box
15745          */
15746         updateProgress : function(value, text){
15747             if(text){
15748                 this.updateText(text);
15749             }
15750             if (pp) { // weird bug on my firefox - for some reason this is not defined
15751                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15752             }
15753             return this;
15754         },        
15755
15756         /**
15757          * Returns true if the message box is currently displayed
15758          * @return {Boolean} True if the message box is visible, else false
15759          */
15760         isVisible : function(){
15761             return dlg && dlg.isVisible();  
15762         },
15763
15764         /**
15765          * Hides the message box if it is displayed
15766          */
15767         hide : function(){
15768             if(this.isVisible()){
15769                 dlg.hide();
15770             }  
15771         },
15772
15773         /**
15774          * Displays a new message box, or reinitializes an existing message box, based on the config options
15775          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15776          * The following config object properties are supported:
15777          * <pre>
15778 Property    Type             Description
15779 ----------  ---------------  ------------------------------------------------------------------------------------
15780 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15781                                    closes (defaults to undefined)
15782 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15783                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15784 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15785                                    progress and wait dialogs will ignore this property and always hide the
15786                                    close button as they can only be closed programmatically.
15787 cls               String           A custom CSS class to apply to the message box element
15788 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15789                                    displayed (defaults to 75)
15790 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15791                                    function will be btn (the name of the button that was clicked, if applicable,
15792                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15793                                    Progress and wait dialogs will ignore this option since they do not respond to
15794                                    user actions and can only be closed programmatically, so any required function
15795                                    should be called by the same code after it closes the dialog.
15796 icon              String           A CSS class that provides a background image to be used as an icon for
15797                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15798 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15799 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15800 modal             Boolean          False to allow user interaction with the page while the message box is
15801                                    displayed (defaults to true)
15802 msg               String           A string that will replace the existing message box body text (defaults
15803                                    to the XHTML-compliant non-breaking space character '&#160;')
15804 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15805 progress          Boolean          True to display a progress bar (defaults to false)
15806 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15807 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15808 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15809 title             String           The title text
15810 value             String           The string value to set into the active textbox element if displayed
15811 wait              Boolean          True to display a progress bar (defaults to false)
15812 width             Number           The width of the dialog in pixels
15813 </pre>
15814          *
15815          * Example usage:
15816          * <pre><code>
15817 Roo.Msg.show({
15818    title: 'Address',
15819    msg: 'Please enter your address:',
15820    width: 300,
15821    buttons: Roo.MessageBox.OKCANCEL,
15822    multiline: true,
15823    fn: saveAddress,
15824    animEl: 'addAddressBtn'
15825 });
15826 </code></pre>
15827          * @param {Object} config Configuration options
15828          * @return {Roo.MessageBox} This message box
15829          */
15830         show : function(options)
15831         {
15832             
15833             // this causes nightmares if you show one dialog after another
15834             // especially on callbacks..
15835              
15836             if(this.isVisible()){
15837                 
15838                 this.hide();
15839                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15840                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15841                 Roo.log("New Dialog Message:" +  options.msg )
15842                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15843                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15844                 
15845             }
15846             var d = this.getDialog();
15847             opt = options;
15848             d.setTitle(opt.title || "&#160;");
15849             d.close.setDisplayed(opt.closable !== false);
15850             activeTextEl = textboxEl;
15851             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15852             if(opt.prompt){
15853                 if(opt.multiline){
15854                     textboxEl.hide();
15855                     textareaEl.show();
15856                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15857                         opt.multiline : this.defaultTextHeight);
15858                     activeTextEl = textareaEl;
15859                 }else{
15860                     textboxEl.show();
15861                     textareaEl.hide();
15862                 }
15863             }else{
15864                 textboxEl.hide();
15865                 textareaEl.hide();
15866             }
15867             progressEl.setDisplayed(opt.progress === true);
15868             this.updateProgress(0);
15869             activeTextEl.dom.value = opt.value || "";
15870             if(opt.prompt){
15871                 dlg.setDefaultButton(activeTextEl);
15872             }else{
15873                 var bs = opt.buttons;
15874                 var db = null;
15875                 if(bs && bs.ok){
15876                     db = buttons["ok"];
15877                 }else if(bs && bs.yes){
15878                     db = buttons["yes"];
15879                 }
15880                 dlg.setDefaultButton(db);
15881             }
15882             bwidth = updateButtons(opt.buttons);
15883             this.updateText(opt.msg);
15884             if(opt.cls){
15885                 d.el.addClass(opt.cls);
15886             }
15887             d.proxyDrag = opt.proxyDrag === true;
15888             d.modal = opt.modal !== false;
15889             d.mask = opt.modal !== false ? mask : false;
15890             if(!d.isVisible()){
15891                 // force it to the end of the z-index stack so it gets a cursor in FF
15892                 document.body.appendChild(dlg.el.dom);
15893                 d.animateTarget = null;
15894                 d.show(options.animEl);
15895             }
15896             return this;
15897         },
15898
15899         /**
15900          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15901          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15902          * and closing the message box when the process is complete.
15903          * @param {String} title The title bar text
15904          * @param {String} msg The message box body text
15905          * @return {Roo.MessageBox} This message box
15906          */
15907         progress : function(title, msg){
15908             this.show({
15909                 title : title,
15910                 msg : msg,
15911                 buttons: false,
15912                 progress:true,
15913                 closable:false,
15914                 minWidth: this.minProgressWidth,
15915                 modal : true
15916             });
15917             return this;
15918         },
15919
15920         /**
15921          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15922          * If a callback function is passed it will be called after the user clicks the button, and the
15923          * id of the button that was clicked will be passed as the only parameter to the callback
15924          * (could also be the top-right close button).
15925          * @param {String} title The title bar text
15926          * @param {String} msg The message box body text
15927          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15928          * @param {Object} scope (optional) The scope of the callback function
15929          * @return {Roo.MessageBox} This message box
15930          */
15931         alert : function(title, msg, fn, scope){
15932             this.show({
15933                 title : title,
15934                 msg : msg,
15935                 buttons: this.OK,
15936                 fn: fn,
15937                 scope : scope,
15938                 modal : true
15939             });
15940             return this;
15941         },
15942
15943         /**
15944          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15945          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15946          * You are responsible for closing the message box when the process is complete.
15947          * @param {String} msg The message box body text
15948          * @param {String} title (optional) The title bar text
15949          * @return {Roo.MessageBox} This message box
15950          */
15951         wait : function(msg, title){
15952             this.show({
15953                 title : title,
15954                 msg : msg,
15955                 buttons: false,
15956                 closable:false,
15957                 progress:true,
15958                 modal:true,
15959                 width:300,
15960                 wait:true
15961             });
15962             waitTimer = Roo.TaskMgr.start({
15963                 run: function(i){
15964                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15965                 },
15966                 interval: 1000
15967             });
15968             return this;
15969         },
15970
15971         /**
15972          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15973          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15974          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15975          * @param {String} title The title bar text
15976          * @param {String} msg The message box body text
15977          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15978          * @param {Object} scope (optional) The scope of the callback function
15979          * @return {Roo.MessageBox} This message box
15980          */
15981         confirm : function(title, msg, fn, scope){
15982             this.show({
15983                 title : title,
15984                 msg : msg,
15985                 buttons: this.YESNO,
15986                 fn: fn,
15987                 scope : scope,
15988                 modal : true
15989             });
15990             return this;
15991         },
15992
15993         /**
15994          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15995          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15996          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15997          * (could also be the top-right close button) and the text that was entered will be passed as the two
15998          * parameters to the callback.
15999          * @param {String} title The title bar text
16000          * @param {String} msg The message box body text
16001          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16002          * @param {Object} scope (optional) The scope of the callback function
16003          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
16004          * property, or the height in pixels to create the textbox (defaults to false / single-line)
16005          * @return {Roo.MessageBox} This message box
16006          */
16007         prompt : function(title, msg, fn, scope, multiline){
16008             this.show({
16009                 title : title,
16010                 msg : msg,
16011                 buttons: this.OKCANCEL,
16012                 fn: fn,
16013                 minWidth:250,
16014                 scope : scope,
16015                 prompt:true,
16016                 multiline: multiline,
16017                 modal : true
16018             });
16019             return this;
16020         },
16021
16022         /**
16023          * Button config that displays a single OK button
16024          * @type Object
16025          */
16026         OK : {ok:true},
16027         /**
16028          * Button config that displays Yes and No buttons
16029          * @type Object
16030          */
16031         YESNO : {yes:true, no:true},
16032         /**
16033          * Button config that displays OK and Cancel buttons
16034          * @type Object
16035          */
16036         OKCANCEL : {ok:true, cancel:true},
16037         /**
16038          * Button config that displays Yes, No and Cancel buttons
16039          * @type Object
16040          */
16041         YESNOCANCEL : {yes:true, no:true, cancel:true},
16042
16043         /**
16044          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16045          * @type Number
16046          */
16047         defaultTextHeight : 75,
16048         /**
16049          * The maximum width in pixels of the message box (defaults to 600)
16050          * @type Number
16051          */
16052         maxWidth : 600,
16053         /**
16054          * The minimum width in pixels of the message box (defaults to 100)
16055          * @type Number
16056          */
16057         minWidth : 100,
16058         /**
16059          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16060          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16061          * @type Number
16062          */
16063         minProgressWidth : 250,
16064         /**
16065          * An object containing the default button text strings that can be overriden for localized language support.
16066          * Supported properties are: ok, cancel, yes and no.
16067          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16068          * @type Object
16069          */
16070         buttonText : {
16071             ok : "OK",
16072             cancel : "Cancel",
16073             yes : "Yes",
16074             no : "No"
16075         }
16076     };
16077 }();
16078
16079 /**
16080  * Shorthand for {@link Roo.MessageBox}
16081  */
16082 Roo.Msg = Roo.MessageBox;/*
16083  * Based on:
16084  * Ext JS Library 1.1.1
16085  * Copyright(c) 2006-2007, Ext JS, LLC.
16086  *
16087  * Originally Released Under LGPL - original licence link has changed is not relivant.
16088  *
16089  * Fork - LGPL
16090  * <script type="text/javascript">
16091  */
16092 /**
16093  * @class Roo.QuickTips
16094  * Provides attractive and customizable tooltips for any element.
16095  * @singleton
16096  */
16097 Roo.QuickTips = function(){
16098     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16099     var ce, bd, xy, dd;
16100     var visible = false, disabled = true, inited = false;
16101     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16102     
16103     var onOver = function(e){
16104         if(disabled){
16105             return;
16106         }
16107         var t = e.getTarget();
16108         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16109             return;
16110         }
16111         if(ce && t == ce.el){
16112             clearTimeout(hideProc);
16113             return;
16114         }
16115         if(t && tagEls[t.id]){
16116             tagEls[t.id].el = t;
16117             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16118             return;
16119         }
16120         var ttp, et = Roo.fly(t);
16121         var ns = cfg.namespace;
16122         if(tm.interceptTitles && t.title){
16123             ttp = t.title;
16124             t.qtip = ttp;
16125             t.removeAttribute("title");
16126             e.preventDefault();
16127         }else{
16128             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16129         }
16130         if(ttp){
16131             showProc = show.defer(tm.showDelay, tm, [{
16132                 el: t, 
16133                 text: ttp, 
16134                 width: et.getAttributeNS(ns, cfg.width),
16135                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16136                 title: et.getAttributeNS(ns, cfg.title),
16137                     cls: et.getAttributeNS(ns, cfg.cls)
16138             }]);
16139         }
16140     };
16141     
16142     var onOut = function(e){
16143         clearTimeout(showProc);
16144         var t = e.getTarget();
16145         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16146             hideProc = setTimeout(hide, tm.hideDelay);
16147         }
16148     };
16149     
16150     var onMove = function(e){
16151         if(disabled){
16152             return;
16153         }
16154         xy = e.getXY();
16155         xy[1] += 18;
16156         if(tm.trackMouse && ce){
16157             el.setXY(xy);
16158         }
16159     };
16160     
16161     var onDown = function(e){
16162         clearTimeout(showProc);
16163         clearTimeout(hideProc);
16164         if(!e.within(el)){
16165             if(tm.hideOnClick){
16166                 hide();
16167                 tm.disable();
16168                 tm.enable.defer(100, tm);
16169             }
16170         }
16171     };
16172     
16173     var getPad = function(){
16174         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16175     };
16176
16177     var show = function(o){
16178         if(disabled){
16179             return;
16180         }
16181         clearTimeout(dismissProc);
16182         ce = o;
16183         if(removeCls){ // in case manually hidden
16184             el.removeClass(removeCls);
16185             removeCls = null;
16186         }
16187         if(ce.cls){
16188             el.addClass(ce.cls);
16189             removeCls = ce.cls;
16190         }
16191         if(ce.title){
16192             tipTitle.update(ce.title);
16193             tipTitle.show();
16194         }else{
16195             tipTitle.update('');
16196             tipTitle.hide();
16197         }
16198         el.dom.style.width  = tm.maxWidth+'px';
16199         //tipBody.dom.style.width = '';
16200         tipBodyText.update(o.text);
16201         var p = getPad(), w = ce.width;
16202         if(!w){
16203             var td = tipBodyText.dom;
16204             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16205             if(aw > tm.maxWidth){
16206                 w = tm.maxWidth;
16207             }else if(aw < tm.minWidth){
16208                 w = tm.minWidth;
16209             }else{
16210                 w = aw;
16211             }
16212         }
16213         //tipBody.setWidth(w);
16214         el.setWidth(parseInt(w, 10) + p);
16215         if(ce.autoHide === false){
16216             close.setDisplayed(true);
16217             if(dd){
16218                 dd.unlock();
16219             }
16220         }else{
16221             close.setDisplayed(false);
16222             if(dd){
16223                 dd.lock();
16224             }
16225         }
16226         if(xy){
16227             el.avoidY = xy[1]-18;
16228             el.setXY(xy);
16229         }
16230         if(tm.animate){
16231             el.setOpacity(.1);
16232             el.setStyle("visibility", "visible");
16233             el.fadeIn({callback: afterShow});
16234         }else{
16235             afterShow();
16236         }
16237     };
16238     
16239     var afterShow = function(){
16240         if(ce){
16241             el.show();
16242             esc.enable();
16243             if(tm.autoDismiss && ce.autoHide !== false){
16244                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16245             }
16246         }
16247     };
16248     
16249     var hide = function(noanim){
16250         clearTimeout(dismissProc);
16251         clearTimeout(hideProc);
16252         ce = null;
16253         if(el.isVisible()){
16254             esc.disable();
16255             if(noanim !== true && tm.animate){
16256                 el.fadeOut({callback: afterHide});
16257             }else{
16258                 afterHide();
16259             } 
16260         }
16261     };
16262     
16263     var afterHide = function(){
16264         el.hide();
16265         if(removeCls){
16266             el.removeClass(removeCls);
16267             removeCls = null;
16268         }
16269     };
16270     
16271     return {
16272         /**
16273         * @cfg {Number} minWidth
16274         * The minimum width of the quick tip (defaults to 40)
16275         */
16276        minWidth : 40,
16277         /**
16278         * @cfg {Number} maxWidth
16279         * The maximum width of the quick tip (defaults to 300)
16280         */
16281        maxWidth : 300,
16282         /**
16283         * @cfg {Boolean} interceptTitles
16284         * True to automatically use the element's DOM title value if available (defaults to false)
16285         */
16286        interceptTitles : false,
16287         /**
16288         * @cfg {Boolean} trackMouse
16289         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16290         */
16291        trackMouse : false,
16292         /**
16293         * @cfg {Boolean} hideOnClick
16294         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16295         */
16296        hideOnClick : true,
16297         /**
16298         * @cfg {Number} showDelay
16299         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16300         */
16301        showDelay : 500,
16302         /**
16303         * @cfg {Number} hideDelay
16304         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16305         */
16306        hideDelay : 200,
16307         /**
16308         * @cfg {Boolean} autoHide
16309         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16310         * Used in conjunction with hideDelay.
16311         */
16312        autoHide : true,
16313         /**
16314         * @cfg {Boolean}
16315         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16316         * (defaults to true).  Used in conjunction with autoDismissDelay.
16317         */
16318        autoDismiss : true,
16319         /**
16320         * @cfg {Number}
16321         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16322         */
16323        autoDismissDelay : 5000,
16324        /**
16325         * @cfg {Boolean} animate
16326         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16327         */
16328        animate : false,
16329
16330        /**
16331         * @cfg {String} title
16332         * Title text to display (defaults to '').  This can be any valid HTML markup.
16333         */
16334         title: '',
16335        /**
16336         * @cfg {String} text
16337         * Body text to display (defaults to '').  This can be any valid HTML markup.
16338         */
16339         text : '',
16340        /**
16341         * @cfg {String} cls
16342         * A CSS class to apply to the base quick tip element (defaults to '').
16343         */
16344         cls : '',
16345        /**
16346         * @cfg {Number} width
16347         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16348         * minWidth or maxWidth.
16349         */
16350         width : null,
16351
16352     /**
16353      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16354      * or display QuickTips in a page.
16355      */
16356        init : function(){
16357           tm = Roo.QuickTips;
16358           cfg = tm.tagConfig;
16359           if(!inited){
16360               if(!Roo.isReady){ // allow calling of init() before onReady
16361                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16362                   return;
16363               }
16364               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16365               el.fxDefaults = {stopFx: true};
16366               // maximum custom styling
16367               //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>');
16368               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>');              
16369               tipTitle = el.child('h3');
16370               tipTitle.enableDisplayMode("block");
16371               tipBody = el.child('div.x-tip-bd');
16372               tipBodyText = el.child('div.x-tip-bd-inner');
16373               //bdLeft = el.child('div.x-tip-bd-left');
16374               //bdRight = el.child('div.x-tip-bd-right');
16375               close = el.child('div.x-tip-close');
16376               close.enableDisplayMode("block");
16377               close.on("click", hide);
16378               var d = Roo.get(document);
16379               d.on("mousedown", onDown);
16380               d.on("mouseover", onOver);
16381               d.on("mouseout", onOut);
16382               d.on("mousemove", onMove);
16383               esc = d.addKeyListener(27, hide);
16384               esc.disable();
16385               if(Roo.dd.DD){
16386                   dd = el.initDD("default", null, {
16387                       onDrag : function(){
16388                           el.sync();  
16389                       }
16390                   });
16391                   dd.setHandleElId(tipTitle.id);
16392                   dd.lock();
16393               }
16394               inited = true;
16395           }
16396           this.enable(); 
16397        },
16398
16399     /**
16400      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16401      * are supported:
16402      * <pre>
16403 Property    Type                   Description
16404 ----------  ---------------------  ------------------------------------------------------------------------
16405 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16406      * </ul>
16407      * @param {Object} config The config object
16408      */
16409        register : function(config){
16410            var cs = config instanceof Array ? config : arguments;
16411            for(var i = 0, len = cs.length; i < len; i++) {
16412                var c = cs[i];
16413                var target = c.target;
16414                if(target){
16415                    if(target instanceof Array){
16416                        for(var j = 0, jlen = target.length; j < jlen; j++){
16417                            tagEls[target[j]] = c;
16418                        }
16419                    }else{
16420                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16421                    }
16422                }
16423            }
16424        },
16425
16426     /**
16427      * Removes this quick tip from its element and destroys it.
16428      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16429      */
16430        unregister : function(el){
16431            delete tagEls[Roo.id(el)];
16432        },
16433
16434     /**
16435      * Enable this quick tip.
16436      */
16437        enable : function(){
16438            if(inited && disabled){
16439                locks.pop();
16440                if(locks.length < 1){
16441                    disabled = false;
16442                }
16443            }
16444        },
16445
16446     /**
16447      * Disable this quick tip.
16448      */
16449        disable : function(){
16450           disabled = true;
16451           clearTimeout(showProc);
16452           clearTimeout(hideProc);
16453           clearTimeout(dismissProc);
16454           if(ce){
16455               hide(true);
16456           }
16457           locks.push(1);
16458        },
16459
16460     /**
16461      * Returns true if the quick tip is enabled, else false.
16462      */
16463        isEnabled : function(){
16464             return !disabled;
16465        },
16466
16467         // private
16468        tagConfig : {
16469            namespace : "ext",
16470            attribute : "qtip",
16471            width : "width",
16472            target : "target",
16473            title : "qtitle",
16474            hide : "hide",
16475            cls : "qclass"
16476        }
16477    };
16478 }();
16479
16480 // backwards compat
16481 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16482  * Based on:
16483  * Ext JS Library 1.1.1
16484  * Copyright(c) 2006-2007, Ext JS, LLC.
16485  *
16486  * Originally Released Under LGPL - original licence link has changed is not relivant.
16487  *
16488  * Fork - LGPL
16489  * <script type="text/javascript">
16490  */
16491  
16492
16493 /**
16494  * @class Roo.tree.TreePanel
16495  * @extends Roo.data.Tree
16496
16497  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16498  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16499  * @cfg {Boolean} enableDD true to enable drag and drop
16500  * @cfg {Boolean} enableDrag true to enable just drag
16501  * @cfg {Boolean} enableDrop true to enable just drop
16502  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16503  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16504  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16505  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16506  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16507  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16508  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16509  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16510  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16511  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16512  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16513  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16514  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16515  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16516  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
16517  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
16518  * 
16519  * @constructor
16520  * @param {String/HTMLElement/Element} el The container element
16521  * @param {Object} config
16522  */
16523 Roo.tree.TreePanel = function(el, config){
16524     var root = false;
16525     var loader = false;
16526     if (config.root) {
16527         root = config.root;
16528         delete config.root;
16529     }
16530     if (config.loader) {
16531         loader = config.loader;
16532         delete config.loader;
16533     }
16534     
16535     Roo.apply(this, config);
16536     Roo.tree.TreePanel.superclass.constructor.call(this);
16537     this.el = Roo.get(el);
16538     this.el.addClass('x-tree');
16539     //console.log(root);
16540     if (root) {
16541         this.setRootNode( Roo.factory(root, Roo.tree));
16542     }
16543     if (loader) {
16544         this.loader = Roo.factory(loader, Roo.tree);
16545     }
16546    /**
16547     * Read-only. The id of the container element becomes this TreePanel's id.
16548     */
16549     this.id = this.el.id;
16550     this.addEvents({
16551         /**
16552         * @event beforeload
16553         * Fires before a node is loaded, return false to cancel
16554         * @param {Node} node The node being loaded
16555         */
16556         "beforeload" : true,
16557         /**
16558         * @event load
16559         * Fires when a node is loaded
16560         * @param {Node} node The node that was loaded
16561         */
16562         "load" : true,
16563         /**
16564         * @event textchange
16565         * Fires when the text for a node is changed
16566         * @param {Node} node The node
16567         * @param {String} text The new text
16568         * @param {String} oldText The old text
16569         */
16570         "textchange" : true,
16571         /**
16572         * @event beforeexpand
16573         * Fires before a node is expanded, return false to cancel.
16574         * @param {Node} node The node
16575         * @param {Boolean} deep
16576         * @param {Boolean} anim
16577         */
16578         "beforeexpand" : true,
16579         /**
16580         * @event beforecollapse
16581         * Fires before a node is collapsed, return false to cancel.
16582         * @param {Node} node The node
16583         * @param {Boolean} deep
16584         * @param {Boolean} anim
16585         */
16586         "beforecollapse" : true,
16587         /**
16588         * @event expand
16589         * Fires when a node is expanded
16590         * @param {Node} node The node
16591         */
16592         "expand" : true,
16593         /**
16594         * @event disabledchange
16595         * Fires when the disabled status of a node changes
16596         * @param {Node} node The node
16597         * @param {Boolean} disabled
16598         */
16599         "disabledchange" : true,
16600         /**
16601         * @event collapse
16602         * Fires when a node is collapsed
16603         * @param {Node} node The node
16604         */
16605         "collapse" : true,
16606         /**
16607         * @event beforeclick
16608         * Fires before click processing on a node. Return false to cancel the default action.
16609         * @param {Node} node The node
16610         * @param {Roo.EventObject} e The event object
16611         */
16612         "beforeclick":true,
16613         /**
16614         * @event checkchange
16615         * Fires when a node with a checkbox's checked property changes
16616         * @param {Node} this This node
16617         * @param {Boolean} checked
16618         */
16619         "checkchange":true,
16620         /**
16621         * @event click
16622         * Fires when a node is clicked
16623         * @param {Node} node The node
16624         * @param {Roo.EventObject} e The event object
16625         */
16626         "click":true,
16627         /**
16628         * @event dblclick
16629         * Fires when a node is double clicked
16630         * @param {Node} node The node
16631         * @param {Roo.EventObject} e The event object
16632         */
16633         "dblclick":true,
16634         /**
16635         * @event contextmenu
16636         * Fires when a node is right clicked
16637         * @param {Node} node The node
16638         * @param {Roo.EventObject} e The event object
16639         */
16640         "contextmenu":true,
16641         /**
16642         * @event beforechildrenrendered
16643         * Fires right before the child nodes for a node are rendered
16644         * @param {Node} node The node
16645         */
16646         "beforechildrenrendered":true,
16647         /**
16648         * @event startdrag
16649         * Fires when a node starts being dragged
16650         * @param {Roo.tree.TreePanel} this
16651         * @param {Roo.tree.TreeNode} node
16652         * @param {event} e The raw browser event
16653         */ 
16654        "startdrag" : true,
16655        /**
16656         * @event enddrag
16657         * Fires when a drag operation is complete
16658         * @param {Roo.tree.TreePanel} this
16659         * @param {Roo.tree.TreeNode} node
16660         * @param {event} e The raw browser event
16661         */
16662        "enddrag" : true,
16663        /**
16664         * @event dragdrop
16665         * Fires when a dragged node is dropped on a valid DD target
16666         * @param {Roo.tree.TreePanel} this
16667         * @param {Roo.tree.TreeNode} node
16668         * @param {DD} dd The dd it was dropped on
16669         * @param {event} e The raw browser event
16670         */
16671        "dragdrop" : true,
16672        /**
16673         * @event beforenodedrop
16674         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16675         * passed to handlers has the following properties:<br />
16676         * <ul style="padding:5px;padding-left:16px;">
16677         * <li>tree - The TreePanel</li>
16678         * <li>target - The node being targeted for the drop</li>
16679         * <li>data - The drag data from the drag source</li>
16680         * <li>point - The point of the drop - append, above or below</li>
16681         * <li>source - The drag source</li>
16682         * <li>rawEvent - Raw mouse event</li>
16683         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16684         * to be inserted by setting them on this object.</li>
16685         * <li>cancel - Set this to true to cancel the drop.</li>
16686         * </ul>
16687         * @param {Object} dropEvent
16688         */
16689        "beforenodedrop" : true,
16690        /**
16691         * @event nodedrop
16692         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16693         * passed to handlers has the following properties:<br />
16694         * <ul style="padding:5px;padding-left:16px;">
16695         * <li>tree - The TreePanel</li>
16696         * <li>target - The node being targeted for the drop</li>
16697         * <li>data - The drag data from the drag source</li>
16698         * <li>point - The point of the drop - append, above or below</li>
16699         * <li>source - The drag source</li>
16700         * <li>rawEvent - Raw mouse event</li>
16701         * <li>dropNode - Dropped node(s).</li>
16702         * </ul>
16703         * @param {Object} dropEvent
16704         */
16705        "nodedrop" : true,
16706         /**
16707         * @event nodedragover
16708         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16709         * passed to handlers has the following properties:<br />
16710         * <ul style="padding:5px;padding-left:16px;">
16711         * <li>tree - The TreePanel</li>
16712         * <li>target - The node being targeted for the drop</li>
16713         * <li>data - The drag data from the drag source</li>
16714         * <li>point - The point of the drop - append, above or below</li>
16715         * <li>source - The drag source</li>
16716         * <li>rawEvent - Raw mouse event</li>
16717         * <li>dropNode - Drop node(s) provided by the source.</li>
16718         * <li>cancel - Set this to true to signal drop not allowed.</li>
16719         * </ul>
16720         * @param {Object} dragOverEvent
16721         */
16722        "nodedragover" : true
16723         
16724     });
16725     if(this.singleExpand){
16726        this.on("beforeexpand", this.restrictExpand, this);
16727     }
16728     if (this.editor) {
16729         this.editor.tree = this;
16730         this.editor = Roo.factory(this.editor, Roo.tree);
16731     }
16732     
16733     if (this.selModel) {
16734         this.selModel = Roo.factory(this.selModel, Roo.tree);
16735     }
16736    
16737 };
16738 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16739     rootVisible : true,
16740     animate: Roo.enableFx,
16741     lines : true,
16742     enableDD : false,
16743     hlDrop : Roo.enableFx,
16744   
16745     renderer: false,
16746     
16747     rendererTip: false,
16748     // private
16749     restrictExpand : function(node){
16750         var p = node.parentNode;
16751         if(p){
16752             if(p.expandedChild && p.expandedChild.parentNode == p){
16753                 p.expandedChild.collapse();
16754             }
16755             p.expandedChild = node;
16756         }
16757     },
16758
16759     // private override
16760     setRootNode : function(node){
16761         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16762         if(!this.rootVisible){
16763             node.ui = new Roo.tree.RootTreeNodeUI(node);
16764         }
16765         return node;
16766     },
16767
16768     /**
16769      * Returns the container element for this TreePanel
16770      */
16771     getEl : function(){
16772         return this.el;
16773     },
16774
16775     /**
16776      * Returns the default TreeLoader for this TreePanel
16777      */
16778     getLoader : function(){
16779         return this.loader;
16780     },
16781
16782     /**
16783      * Expand all nodes
16784      */
16785     expandAll : function(){
16786         this.root.expand(true);
16787     },
16788
16789     /**
16790      * Collapse all nodes
16791      */
16792     collapseAll : function(){
16793         this.root.collapse(true);
16794     },
16795
16796     /**
16797      * Returns the selection model used by this TreePanel
16798      */
16799     getSelectionModel : function(){
16800         if(!this.selModel){
16801             this.selModel = new Roo.tree.DefaultSelectionModel();
16802         }
16803         return this.selModel;
16804     },
16805
16806     /**
16807      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16808      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16809      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16810      * @return {Array}
16811      */
16812     getChecked : function(a, startNode){
16813         startNode = startNode || this.root;
16814         var r = [];
16815         var f = function(){
16816             if(this.attributes.checked){
16817                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16818             }
16819         }
16820         startNode.cascade(f);
16821         return r;
16822     },
16823
16824     /**
16825      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16826      * @param {String} path
16827      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16828      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16829      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16830      */
16831     expandPath : function(path, attr, callback){
16832         attr = attr || "id";
16833         var keys = path.split(this.pathSeparator);
16834         var curNode = this.root;
16835         if(curNode.attributes[attr] != keys[1]){ // invalid root
16836             if(callback){
16837                 callback(false, null);
16838             }
16839             return;
16840         }
16841         var index = 1;
16842         var f = function(){
16843             if(++index == keys.length){
16844                 if(callback){
16845                     callback(true, curNode);
16846                 }
16847                 return;
16848             }
16849             var c = curNode.findChild(attr, keys[index]);
16850             if(!c){
16851                 if(callback){
16852                     callback(false, curNode);
16853                 }
16854                 return;
16855             }
16856             curNode = c;
16857             c.expand(false, false, f);
16858         };
16859         curNode.expand(false, false, f);
16860     },
16861
16862     /**
16863      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16864      * @param {String} path
16865      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16866      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16867      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16868      */
16869     selectPath : function(path, attr, callback){
16870         attr = attr || "id";
16871         var keys = path.split(this.pathSeparator);
16872         var v = keys.pop();
16873         if(keys.length > 0){
16874             var f = function(success, node){
16875                 if(success && node){
16876                     var n = node.findChild(attr, v);
16877                     if(n){
16878                         n.select();
16879                         if(callback){
16880                             callback(true, n);
16881                         }
16882                     }else if(callback){
16883                         callback(false, n);
16884                     }
16885                 }else{
16886                     if(callback){
16887                         callback(false, n);
16888                     }
16889                 }
16890             };
16891             this.expandPath(keys.join(this.pathSeparator), attr, f);
16892         }else{
16893             this.root.select();
16894             if(callback){
16895                 callback(true, this.root);
16896             }
16897         }
16898     },
16899
16900     getTreeEl : function(){
16901         return this.el;
16902     },
16903
16904     /**
16905      * Trigger rendering of this TreePanel
16906      */
16907     render : function(){
16908         if (this.innerCt) {
16909             return this; // stop it rendering more than once!!
16910         }
16911         
16912         this.innerCt = this.el.createChild({tag:"ul",
16913                cls:"x-tree-root-ct " +
16914                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16915
16916         if(this.containerScroll){
16917             Roo.dd.ScrollManager.register(this.el);
16918         }
16919         if((this.enableDD || this.enableDrop) && !this.dropZone){
16920            /**
16921             * The dropZone used by this tree if drop is enabled
16922             * @type Roo.tree.TreeDropZone
16923             */
16924              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16925                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16926            });
16927         }
16928         if((this.enableDD || this.enableDrag) && !this.dragZone){
16929            /**
16930             * The dragZone used by this tree if drag is enabled
16931             * @type Roo.tree.TreeDragZone
16932             */
16933             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16934                ddGroup: this.ddGroup || "TreeDD",
16935                scroll: this.ddScroll
16936            });
16937         }
16938         this.getSelectionModel().init(this);
16939         if (!this.root) {
16940             Roo.log("ROOT not set in tree");
16941             return this;
16942         }
16943         this.root.render();
16944         if(!this.rootVisible){
16945             this.root.renderChildren();
16946         }
16947         return this;
16948     }
16949 });/*
16950  * Based on:
16951  * Ext JS Library 1.1.1
16952  * Copyright(c) 2006-2007, Ext JS, LLC.
16953  *
16954  * Originally Released Under LGPL - original licence link has changed is not relivant.
16955  *
16956  * Fork - LGPL
16957  * <script type="text/javascript">
16958  */
16959  
16960
16961 /**
16962  * @class Roo.tree.DefaultSelectionModel
16963  * @extends Roo.util.Observable
16964  * The default single selection for a TreePanel.
16965  * @param {Object} cfg Configuration
16966  */
16967 Roo.tree.DefaultSelectionModel = function(cfg){
16968    this.selNode = null;
16969    
16970    
16971    
16972    this.addEvents({
16973        /**
16974         * @event selectionchange
16975         * Fires when the selected node changes
16976         * @param {DefaultSelectionModel} this
16977         * @param {TreeNode} node the new selection
16978         */
16979        "selectionchange" : true,
16980
16981        /**
16982         * @event beforeselect
16983         * Fires before the selected node changes, return false to cancel the change
16984         * @param {DefaultSelectionModel} this
16985         * @param {TreeNode} node the new selection
16986         * @param {TreeNode} node the old selection
16987         */
16988        "beforeselect" : true
16989    });
16990    
16991     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16992 };
16993
16994 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16995     init : function(tree){
16996         this.tree = tree;
16997         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16998         tree.on("click", this.onNodeClick, this);
16999     },
17000     
17001     onNodeClick : function(node, e){
17002         if (e.ctrlKey && this.selNode == node)  {
17003             this.unselect(node);
17004             return;
17005         }
17006         this.select(node);
17007     },
17008     
17009     /**
17010      * Select a node.
17011      * @param {TreeNode} node The node to select
17012      * @return {TreeNode} The selected node
17013      */
17014     select : function(node){
17015         var last = this.selNode;
17016         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17017             if(last){
17018                 last.ui.onSelectedChange(false);
17019             }
17020             this.selNode = node;
17021             node.ui.onSelectedChange(true);
17022             this.fireEvent("selectionchange", this, node, last);
17023         }
17024         return node;
17025     },
17026     
17027     /**
17028      * Deselect a node.
17029      * @param {TreeNode} node The node to unselect
17030      */
17031     unselect : function(node){
17032         if(this.selNode == node){
17033             this.clearSelections();
17034         }    
17035     },
17036     
17037     /**
17038      * Clear all selections
17039      */
17040     clearSelections : function(){
17041         var n = this.selNode;
17042         if(n){
17043             n.ui.onSelectedChange(false);
17044             this.selNode = null;
17045             this.fireEvent("selectionchange", this, null);
17046         }
17047         return n;
17048     },
17049     
17050     /**
17051      * Get the selected node
17052      * @return {TreeNode} The selected node
17053      */
17054     getSelectedNode : function(){
17055         return this.selNode;    
17056     },
17057     
17058     /**
17059      * Returns true if the node is selected
17060      * @param {TreeNode} node The node to check
17061      * @return {Boolean}
17062      */
17063     isSelected : function(node){
17064         return this.selNode == node;  
17065     },
17066
17067     /**
17068      * Selects the node above the selected node in the tree, intelligently walking the nodes
17069      * @return TreeNode The new selection
17070      */
17071     selectPrevious : function(){
17072         var s = this.selNode || this.lastSelNode;
17073         if(!s){
17074             return null;
17075         }
17076         var ps = s.previousSibling;
17077         if(ps){
17078             if(!ps.isExpanded() || ps.childNodes.length < 1){
17079                 return this.select(ps);
17080             } else{
17081                 var lc = ps.lastChild;
17082                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17083                     lc = lc.lastChild;
17084                 }
17085                 return this.select(lc);
17086             }
17087         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17088             return this.select(s.parentNode);
17089         }
17090         return null;
17091     },
17092
17093     /**
17094      * Selects the node above the selected node in the tree, intelligently walking the nodes
17095      * @return TreeNode The new selection
17096      */
17097     selectNext : function(){
17098         var s = this.selNode || this.lastSelNode;
17099         if(!s){
17100             return null;
17101         }
17102         if(s.firstChild && s.isExpanded()){
17103              return this.select(s.firstChild);
17104          }else if(s.nextSibling){
17105              return this.select(s.nextSibling);
17106          }else if(s.parentNode){
17107             var newS = null;
17108             s.parentNode.bubble(function(){
17109                 if(this.nextSibling){
17110                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17111                     return false;
17112                 }
17113             });
17114             return newS;
17115          }
17116         return null;
17117     },
17118
17119     onKeyDown : function(e){
17120         var s = this.selNode || this.lastSelNode;
17121         // undesirable, but required
17122         var sm = this;
17123         if(!s){
17124             return;
17125         }
17126         var k = e.getKey();
17127         switch(k){
17128              case e.DOWN:
17129                  e.stopEvent();
17130                  this.selectNext();
17131              break;
17132              case e.UP:
17133                  e.stopEvent();
17134                  this.selectPrevious();
17135              break;
17136              case e.RIGHT:
17137                  e.preventDefault();
17138                  if(s.hasChildNodes()){
17139                      if(!s.isExpanded()){
17140                          s.expand();
17141                      }else if(s.firstChild){
17142                          this.select(s.firstChild, e);
17143                      }
17144                  }
17145              break;
17146              case e.LEFT:
17147                  e.preventDefault();
17148                  if(s.hasChildNodes() && s.isExpanded()){
17149                      s.collapse();
17150                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17151                      this.select(s.parentNode, e);
17152                  }
17153              break;
17154         };
17155     }
17156 });
17157
17158 /**
17159  * @class Roo.tree.MultiSelectionModel
17160  * @extends Roo.util.Observable
17161  * Multi selection for a TreePanel.
17162  * @param {Object} cfg Configuration
17163  */
17164 Roo.tree.MultiSelectionModel = function(){
17165    this.selNodes = [];
17166    this.selMap = {};
17167    this.addEvents({
17168        /**
17169         * @event selectionchange
17170         * Fires when the selected nodes change
17171         * @param {MultiSelectionModel} this
17172         * @param {Array} nodes Array of the selected nodes
17173         */
17174        "selectionchange" : true
17175    });
17176    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17177    
17178 };
17179
17180 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17181     init : function(tree){
17182         this.tree = tree;
17183         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17184         tree.on("click", this.onNodeClick, this);
17185     },
17186     
17187     onNodeClick : function(node, e){
17188         this.select(node, e, e.ctrlKey);
17189     },
17190     
17191     /**
17192      * Select a node.
17193      * @param {TreeNode} node The node to select
17194      * @param {EventObject} e (optional) An event associated with the selection
17195      * @param {Boolean} keepExisting True to retain existing selections
17196      * @return {TreeNode} The selected node
17197      */
17198     select : function(node, e, keepExisting){
17199         if(keepExisting !== true){
17200             this.clearSelections(true);
17201         }
17202         if(this.isSelected(node)){
17203             this.lastSelNode = node;
17204             return node;
17205         }
17206         this.selNodes.push(node);
17207         this.selMap[node.id] = node;
17208         this.lastSelNode = node;
17209         node.ui.onSelectedChange(true);
17210         this.fireEvent("selectionchange", this, this.selNodes);
17211         return node;
17212     },
17213     
17214     /**
17215      * Deselect a node.
17216      * @param {TreeNode} node The node to unselect
17217      */
17218     unselect : function(node){
17219         if(this.selMap[node.id]){
17220             node.ui.onSelectedChange(false);
17221             var sn = this.selNodes;
17222             var index = -1;
17223             if(sn.indexOf){
17224                 index = sn.indexOf(node);
17225             }else{
17226                 for(var i = 0, len = sn.length; i < len; i++){
17227                     if(sn[i] == node){
17228                         index = i;
17229                         break;
17230                     }
17231                 }
17232             }
17233             if(index != -1){
17234                 this.selNodes.splice(index, 1);
17235             }
17236             delete this.selMap[node.id];
17237             this.fireEvent("selectionchange", this, this.selNodes);
17238         }
17239     },
17240     
17241     /**
17242      * Clear all selections
17243      */
17244     clearSelections : function(suppressEvent){
17245         var sn = this.selNodes;
17246         if(sn.length > 0){
17247             for(var i = 0, len = sn.length; i < len; i++){
17248                 sn[i].ui.onSelectedChange(false);
17249             }
17250             this.selNodes = [];
17251             this.selMap = {};
17252             if(suppressEvent !== true){
17253                 this.fireEvent("selectionchange", this, this.selNodes);
17254             }
17255         }
17256     },
17257     
17258     /**
17259      * Returns true if the node is selected
17260      * @param {TreeNode} node The node to check
17261      * @return {Boolean}
17262      */
17263     isSelected : function(node){
17264         return this.selMap[node.id] ? true : false;  
17265     },
17266     
17267     /**
17268      * Returns an array of the selected nodes
17269      * @return {Array}
17270      */
17271     getSelectedNodes : function(){
17272         return this.selNodes;    
17273     },
17274
17275     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17276
17277     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17278
17279     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17280 });/*
17281  * Based on:
17282  * Ext JS Library 1.1.1
17283  * Copyright(c) 2006-2007, Ext JS, LLC.
17284  *
17285  * Originally Released Under LGPL - original licence link has changed is not relivant.
17286  *
17287  * Fork - LGPL
17288  * <script type="text/javascript">
17289  */
17290  
17291 /**
17292  * @class Roo.tree.TreeNode
17293  * @extends Roo.data.Node
17294  * @cfg {String} text The text for this node
17295  * @cfg {Boolean} expanded true to start the node expanded
17296  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17297  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17298  * @cfg {Boolean} disabled true to start the node disabled
17299  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17300  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17301  * @cfg {String} cls A css class to be added to the node
17302  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17303  * @cfg {String} href URL of the link used for the node (defaults to #)
17304  * @cfg {String} hrefTarget target frame for the link
17305  * @cfg {String} qtip An Ext QuickTip for the node
17306  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17307  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17308  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17309  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17310  * (defaults to undefined with no checkbox rendered)
17311  * @constructor
17312  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17313  */
17314 Roo.tree.TreeNode = function(attributes){
17315     attributes = attributes || {};
17316     if(typeof attributes == "string"){
17317         attributes = {text: attributes};
17318     }
17319     this.childrenRendered = false;
17320     this.rendered = false;
17321     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17322     this.expanded = attributes.expanded === true;
17323     this.isTarget = attributes.isTarget !== false;
17324     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17325     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17326
17327     /**
17328      * Read-only. The text for this node. To change it use setText().
17329      * @type String
17330      */
17331     this.text = attributes.text;
17332     /**
17333      * True if this node is disabled.
17334      * @type Boolean
17335      */
17336     this.disabled = attributes.disabled === true;
17337
17338     this.addEvents({
17339         /**
17340         * @event textchange
17341         * Fires when the text for this node is changed
17342         * @param {Node} this This node
17343         * @param {String} text The new text
17344         * @param {String} oldText The old text
17345         */
17346         "textchange" : true,
17347         /**
17348         * @event beforeexpand
17349         * Fires before this node is expanded, return false to cancel.
17350         * @param {Node} this This node
17351         * @param {Boolean} deep
17352         * @param {Boolean} anim
17353         */
17354         "beforeexpand" : true,
17355         /**
17356         * @event beforecollapse
17357         * Fires before this node is collapsed, return false to cancel.
17358         * @param {Node} this This node
17359         * @param {Boolean} deep
17360         * @param {Boolean} anim
17361         */
17362         "beforecollapse" : true,
17363         /**
17364         * @event expand
17365         * Fires when this node is expanded
17366         * @param {Node} this This node
17367         */
17368         "expand" : true,
17369         /**
17370         * @event disabledchange
17371         * Fires when the disabled status of this node changes
17372         * @param {Node} this This node
17373         * @param {Boolean} disabled
17374         */
17375         "disabledchange" : true,
17376         /**
17377         * @event collapse
17378         * Fires when this node is collapsed
17379         * @param {Node} this This node
17380         */
17381         "collapse" : true,
17382         /**
17383         * @event beforeclick
17384         * Fires before click processing. Return false to cancel the default action.
17385         * @param {Node} this This node
17386         * @param {Roo.EventObject} e The event object
17387         */
17388         "beforeclick":true,
17389         /**
17390         * @event checkchange
17391         * Fires when a node with a checkbox's checked property changes
17392         * @param {Node} this This node
17393         * @param {Boolean} checked
17394         */
17395         "checkchange":true,
17396         /**
17397         * @event click
17398         * Fires when this node is clicked
17399         * @param {Node} this This node
17400         * @param {Roo.EventObject} e The event object
17401         */
17402         "click":true,
17403         /**
17404         * @event dblclick
17405         * Fires when this node is double clicked
17406         * @param {Node} this This node
17407         * @param {Roo.EventObject} e The event object
17408         */
17409         "dblclick":true,
17410         /**
17411         * @event contextmenu
17412         * Fires when this node is right clicked
17413         * @param {Node} this This node
17414         * @param {Roo.EventObject} e The event object
17415         */
17416         "contextmenu":true,
17417         /**
17418         * @event beforechildrenrendered
17419         * Fires right before the child nodes for this node are rendered
17420         * @param {Node} this This node
17421         */
17422         "beforechildrenrendered":true
17423     });
17424
17425     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17426
17427     /**
17428      * Read-only. The UI for this node
17429      * @type TreeNodeUI
17430      */
17431     this.ui = new uiClass(this);
17432     
17433     // finally support items[]
17434     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17435         return;
17436     }
17437     
17438     
17439     Roo.each(this.attributes.items, function(c) {
17440         this.appendChild(Roo.factory(c,Roo.Tree));
17441     }, this);
17442     delete this.attributes.items;
17443     
17444     
17445     
17446 };
17447 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17448     preventHScroll: true,
17449     /**
17450      * Returns true if this node is expanded
17451      * @return {Boolean}
17452      */
17453     isExpanded : function(){
17454         return this.expanded;
17455     },
17456
17457     /**
17458      * Returns the UI object for this node
17459      * @return {TreeNodeUI}
17460      */
17461     getUI : function(){
17462         return this.ui;
17463     },
17464
17465     // private override
17466     setFirstChild : function(node){
17467         var of = this.firstChild;
17468         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17469         if(this.childrenRendered && of && node != of){
17470             of.renderIndent(true, true);
17471         }
17472         if(this.rendered){
17473             this.renderIndent(true, true);
17474         }
17475     },
17476
17477     // private override
17478     setLastChild : function(node){
17479         var ol = this.lastChild;
17480         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17481         if(this.childrenRendered && ol && node != ol){
17482             ol.renderIndent(true, true);
17483         }
17484         if(this.rendered){
17485             this.renderIndent(true, true);
17486         }
17487     },
17488
17489     // these methods are overridden to provide lazy rendering support
17490     // private override
17491     appendChild : function()
17492     {
17493         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17494         if(node && this.childrenRendered){
17495             node.render();
17496         }
17497         this.ui.updateExpandIcon();
17498         return node;
17499     },
17500
17501     // private override
17502     removeChild : function(node){
17503         this.ownerTree.getSelectionModel().unselect(node);
17504         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17505         // if it's been rendered remove dom node
17506         if(this.childrenRendered){
17507             node.ui.remove();
17508         }
17509         if(this.childNodes.length < 1){
17510             this.collapse(false, false);
17511         }else{
17512             this.ui.updateExpandIcon();
17513         }
17514         if(!this.firstChild) {
17515             this.childrenRendered = false;
17516         }
17517         return node;
17518     },
17519
17520     // private override
17521     insertBefore : function(node, refNode){
17522         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17523         if(newNode && refNode && this.childrenRendered){
17524             node.render();
17525         }
17526         this.ui.updateExpandIcon();
17527         return newNode;
17528     },
17529
17530     /**
17531      * Sets the text for this node
17532      * @param {String} text
17533      */
17534     setText : function(text){
17535         var oldText = this.text;
17536         this.text = text;
17537         this.attributes.text = text;
17538         if(this.rendered){ // event without subscribing
17539             this.ui.onTextChange(this, text, oldText);
17540         }
17541         this.fireEvent("textchange", this, text, oldText);
17542     },
17543
17544     /**
17545      * Triggers selection of this node
17546      */
17547     select : function(){
17548         this.getOwnerTree().getSelectionModel().select(this);
17549     },
17550
17551     /**
17552      * Triggers deselection of this node
17553      */
17554     unselect : function(){
17555         this.getOwnerTree().getSelectionModel().unselect(this);
17556     },
17557
17558     /**
17559      * Returns true if this node is selected
17560      * @return {Boolean}
17561      */
17562     isSelected : function(){
17563         return this.getOwnerTree().getSelectionModel().isSelected(this);
17564     },
17565
17566     /**
17567      * Expand this node.
17568      * @param {Boolean} deep (optional) True to expand all children as well
17569      * @param {Boolean} anim (optional) false to cancel the default animation
17570      * @param {Function} callback (optional) A callback to be called when
17571      * expanding this node completes (does not wait for deep expand to complete).
17572      * Called with 1 parameter, this node.
17573      */
17574     expand : function(deep, anim, callback){
17575         if(!this.expanded){
17576             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17577                 return;
17578             }
17579             if(!this.childrenRendered){
17580                 this.renderChildren();
17581             }
17582             this.expanded = true;
17583             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17584                 this.ui.animExpand(function(){
17585                     this.fireEvent("expand", this);
17586                     if(typeof callback == "function"){
17587                         callback(this);
17588                     }
17589                     if(deep === true){
17590                         this.expandChildNodes(true);
17591                     }
17592                 }.createDelegate(this));
17593                 return;
17594             }else{
17595                 this.ui.expand();
17596                 this.fireEvent("expand", this);
17597                 if(typeof callback == "function"){
17598                     callback(this);
17599                 }
17600             }
17601         }else{
17602            if(typeof callback == "function"){
17603                callback(this);
17604            }
17605         }
17606         if(deep === true){
17607             this.expandChildNodes(true);
17608         }
17609     },
17610
17611     isHiddenRoot : function(){
17612         return this.isRoot && !this.getOwnerTree().rootVisible;
17613     },
17614
17615     /**
17616      * Collapse this node.
17617      * @param {Boolean} deep (optional) True to collapse all children as well
17618      * @param {Boolean} anim (optional) false to cancel the default animation
17619      */
17620     collapse : function(deep, anim){
17621         if(this.expanded && !this.isHiddenRoot()){
17622             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17623                 return;
17624             }
17625             this.expanded = false;
17626             if((this.getOwnerTree().animate && anim !== false) || anim){
17627                 this.ui.animCollapse(function(){
17628                     this.fireEvent("collapse", this);
17629                     if(deep === true){
17630                         this.collapseChildNodes(true);
17631                     }
17632                 }.createDelegate(this));
17633                 return;
17634             }else{
17635                 this.ui.collapse();
17636                 this.fireEvent("collapse", this);
17637             }
17638         }
17639         if(deep === true){
17640             var cs = this.childNodes;
17641             for(var i = 0, len = cs.length; i < len; i++) {
17642                 cs[i].collapse(true, false);
17643             }
17644         }
17645     },
17646
17647     // private
17648     delayedExpand : function(delay){
17649         if(!this.expandProcId){
17650             this.expandProcId = this.expand.defer(delay, this);
17651         }
17652     },
17653
17654     // private
17655     cancelExpand : function(){
17656         if(this.expandProcId){
17657             clearTimeout(this.expandProcId);
17658         }
17659         this.expandProcId = false;
17660     },
17661
17662     /**
17663      * Toggles expanded/collapsed state of the node
17664      */
17665     toggle : function(){
17666         if(this.expanded){
17667             this.collapse();
17668         }else{
17669             this.expand();
17670         }
17671     },
17672
17673     /**
17674      * Ensures all parent nodes are expanded
17675      */
17676     ensureVisible : function(callback){
17677         var tree = this.getOwnerTree();
17678         tree.expandPath(this.parentNode.getPath(), false, function(){
17679             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17680             Roo.callback(callback);
17681         }.createDelegate(this));
17682     },
17683
17684     /**
17685      * Expand all child nodes
17686      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17687      */
17688     expandChildNodes : function(deep){
17689         var cs = this.childNodes;
17690         for(var i = 0, len = cs.length; i < len; i++) {
17691                 cs[i].expand(deep);
17692         }
17693     },
17694
17695     /**
17696      * Collapse all child nodes
17697      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17698      */
17699     collapseChildNodes : function(deep){
17700         var cs = this.childNodes;
17701         for(var i = 0, len = cs.length; i < len; i++) {
17702                 cs[i].collapse(deep);
17703         }
17704     },
17705
17706     /**
17707      * Disables this node
17708      */
17709     disable : function(){
17710         this.disabled = true;
17711         this.unselect();
17712         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17713             this.ui.onDisableChange(this, true);
17714         }
17715         this.fireEvent("disabledchange", this, true);
17716     },
17717
17718     /**
17719      * Enables this node
17720      */
17721     enable : function(){
17722         this.disabled = false;
17723         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17724             this.ui.onDisableChange(this, false);
17725         }
17726         this.fireEvent("disabledchange", this, false);
17727     },
17728
17729     // private
17730     renderChildren : function(suppressEvent){
17731         if(suppressEvent !== false){
17732             this.fireEvent("beforechildrenrendered", this);
17733         }
17734         var cs = this.childNodes;
17735         for(var i = 0, len = cs.length; i < len; i++){
17736             cs[i].render(true);
17737         }
17738         this.childrenRendered = true;
17739     },
17740
17741     // private
17742     sort : function(fn, scope){
17743         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17744         if(this.childrenRendered){
17745             var cs = this.childNodes;
17746             for(var i = 0, len = cs.length; i < len; i++){
17747                 cs[i].render(true);
17748             }
17749         }
17750     },
17751
17752     // private
17753     render : function(bulkRender){
17754         this.ui.render(bulkRender);
17755         if(!this.rendered){
17756             this.rendered = true;
17757             if(this.expanded){
17758                 this.expanded = false;
17759                 this.expand(false, false);
17760             }
17761         }
17762     },
17763
17764     // private
17765     renderIndent : function(deep, refresh){
17766         if(refresh){
17767             this.ui.childIndent = null;
17768         }
17769         this.ui.renderIndent();
17770         if(deep === true && this.childrenRendered){
17771             var cs = this.childNodes;
17772             for(var i = 0, len = cs.length; i < len; i++){
17773                 cs[i].renderIndent(true, refresh);
17774             }
17775         }
17776     }
17777 });/*
17778  * Based on:
17779  * Ext JS Library 1.1.1
17780  * Copyright(c) 2006-2007, Ext JS, LLC.
17781  *
17782  * Originally Released Under LGPL - original licence link has changed is not relivant.
17783  *
17784  * Fork - LGPL
17785  * <script type="text/javascript">
17786  */
17787  
17788 /**
17789  * @class Roo.tree.AsyncTreeNode
17790  * @extends Roo.tree.TreeNode
17791  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17792  * @constructor
17793  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17794  */
17795  Roo.tree.AsyncTreeNode = function(config){
17796     this.loaded = false;
17797     this.loading = false;
17798     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17799     /**
17800     * @event beforeload
17801     * Fires before this node is loaded, return false to cancel
17802     * @param {Node} this This node
17803     */
17804     this.addEvents({'beforeload':true, 'load': true});
17805     /**
17806     * @event load
17807     * Fires when this node is loaded
17808     * @param {Node} this This node
17809     */
17810     /**
17811      * The loader used by this node (defaults to using the tree's defined loader)
17812      * @type TreeLoader
17813      * @property loader
17814      */
17815 };
17816 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17817     expand : function(deep, anim, callback){
17818         if(this.loading){ // if an async load is already running, waiting til it's done
17819             var timer;
17820             var f = function(){
17821                 if(!this.loading){ // done loading
17822                     clearInterval(timer);
17823                     this.expand(deep, anim, callback);
17824                 }
17825             }.createDelegate(this);
17826             timer = setInterval(f, 200);
17827             return;
17828         }
17829         if(!this.loaded){
17830             if(this.fireEvent("beforeload", this) === false){
17831                 return;
17832             }
17833             this.loading = true;
17834             this.ui.beforeLoad(this);
17835             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17836             if(loader){
17837                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17838                 return;
17839             }
17840         }
17841         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17842     },
17843     
17844     /**
17845      * Returns true if this node is currently loading
17846      * @return {Boolean}
17847      */
17848     isLoading : function(){
17849         return this.loading;  
17850     },
17851     
17852     loadComplete : function(deep, anim, callback){
17853         this.loading = false;
17854         this.loaded = true;
17855         this.ui.afterLoad(this);
17856         this.fireEvent("load", this);
17857         this.expand(deep, anim, callback);
17858     },
17859     
17860     /**
17861      * Returns true if this node has been loaded
17862      * @return {Boolean}
17863      */
17864     isLoaded : function(){
17865         return this.loaded;
17866     },
17867     
17868     hasChildNodes : function(){
17869         if(!this.isLeaf() && !this.loaded){
17870             return true;
17871         }else{
17872             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17873         }
17874     },
17875
17876     /**
17877      * Trigger a reload for this node
17878      * @param {Function} callback
17879      */
17880     reload : function(callback){
17881         this.collapse(false, false);
17882         while(this.firstChild){
17883             this.removeChild(this.firstChild);
17884         }
17885         this.childrenRendered = false;
17886         this.loaded = false;
17887         if(this.isHiddenRoot()){
17888             this.expanded = false;
17889         }
17890         this.expand(false, false, callback);
17891     }
17892 });/*
17893  * Based on:
17894  * Ext JS Library 1.1.1
17895  * Copyright(c) 2006-2007, Ext JS, LLC.
17896  *
17897  * Originally Released Under LGPL - original licence link has changed is not relivant.
17898  *
17899  * Fork - LGPL
17900  * <script type="text/javascript">
17901  */
17902  
17903 /**
17904  * @class Roo.tree.TreeNodeUI
17905  * @constructor
17906  * @param {Object} node The node to render
17907  * The TreeNode UI implementation is separate from the
17908  * tree implementation. Unless you are customizing the tree UI,
17909  * you should never have to use this directly.
17910  */
17911 Roo.tree.TreeNodeUI = function(node){
17912     this.node = node;
17913     this.rendered = false;
17914     this.animating = false;
17915     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17916 };
17917
17918 Roo.tree.TreeNodeUI.prototype = {
17919     removeChild : function(node){
17920         if(this.rendered){
17921             this.ctNode.removeChild(node.ui.getEl());
17922         }
17923     },
17924
17925     beforeLoad : function(){
17926          this.addClass("x-tree-node-loading");
17927     },
17928
17929     afterLoad : function(){
17930          this.removeClass("x-tree-node-loading");
17931     },
17932
17933     onTextChange : function(node, text, oldText){
17934         if(this.rendered){
17935             this.textNode.innerHTML = text;
17936         }
17937     },
17938
17939     onDisableChange : function(node, state){
17940         this.disabled = state;
17941         if(state){
17942             this.addClass("x-tree-node-disabled");
17943         }else{
17944             this.removeClass("x-tree-node-disabled");
17945         }
17946     },
17947
17948     onSelectedChange : function(state){
17949         if(state){
17950             this.focus();
17951             this.addClass("x-tree-selected");
17952         }else{
17953             //this.blur();
17954             this.removeClass("x-tree-selected");
17955         }
17956     },
17957
17958     onMove : function(tree, node, oldParent, newParent, index, refNode){
17959         this.childIndent = null;
17960         if(this.rendered){
17961             var targetNode = newParent.ui.getContainer();
17962             if(!targetNode){//target not rendered
17963                 this.holder = document.createElement("div");
17964                 this.holder.appendChild(this.wrap);
17965                 return;
17966             }
17967             var insertBefore = refNode ? refNode.ui.getEl() : null;
17968             if(insertBefore){
17969                 targetNode.insertBefore(this.wrap, insertBefore);
17970             }else{
17971                 targetNode.appendChild(this.wrap);
17972             }
17973             this.node.renderIndent(true);
17974         }
17975     },
17976
17977     addClass : function(cls){
17978         if(this.elNode){
17979             Roo.fly(this.elNode).addClass(cls);
17980         }
17981     },
17982
17983     removeClass : function(cls){
17984         if(this.elNode){
17985             Roo.fly(this.elNode).removeClass(cls);
17986         }
17987     },
17988
17989     remove : function(){
17990         if(this.rendered){
17991             this.holder = document.createElement("div");
17992             this.holder.appendChild(this.wrap);
17993         }
17994     },
17995
17996     fireEvent : function(){
17997         return this.node.fireEvent.apply(this.node, arguments);
17998     },
17999
18000     initEvents : function(){
18001         this.node.on("move", this.onMove, this);
18002         var E = Roo.EventManager;
18003         var a = this.anchor;
18004
18005         var el = Roo.fly(a, '_treeui');
18006
18007         if(Roo.isOpera){ // opera render bug ignores the CSS
18008             el.setStyle("text-decoration", "none");
18009         }
18010
18011         el.on("click", this.onClick, this);
18012         el.on("dblclick", this.onDblClick, this);
18013
18014         if(this.checkbox){
18015             Roo.EventManager.on(this.checkbox,
18016                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18017         }
18018
18019         el.on("contextmenu", this.onContextMenu, this);
18020
18021         var icon = Roo.fly(this.iconNode);
18022         icon.on("click", this.onClick, this);
18023         icon.on("dblclick", this.onDblClick, this);
18024         icon.on("contextmenu", this.onContextMenu, this);
18025         E.on(this.ecNode, "click", this.ecClick, this, true);
18026
18027         if(this.node.disabled){
18028             this.addClass("x-tree-node-disabled");
18029         }
18030         if(this.node.hidden){
18031             this.addClass("x-tree-node-disabled");
18032         }
18033         var ot = this.node.getOwnerTree();
18034         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18035         if(dd && (!this.node.isRoot || ot.rootVisible)){
18036             Roo.dd.Registry.register(this.elNode, {
18037                 node: this.node,
18038                 handles: this.getDDHandles(),
18039                 isHandle: false
18040             });
18041         }
18042     },
18043
18044     getDDHandles : function(){
18045         return [this.iconNode, this.textNode];
18046     },
18047
18048     hide : function(){
18049         if(this.rendered){
18050             this.wrap.style.display = "none";
18051         }
18052     },
18053
18054     show : function(){
18055         if(this.rendered){
18056             this.wrap.style.display = "";
18057         }
18058     },
18059
18060     onContextMenu : function(e){
18061         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18062             e.preventDefault();
18063             this.focus();
18064             this.fireEvent("contextmenu", this.node, e);
18065         }
18066     },
18067
18068     onClick : function(e){
18069         if(this.dropping){
18070             e.stopEvent();
18071             return;
18072         }
18073         if(this.fireEvent("beforeclick", this.node, e) !== false){
18074             if(!this.disabled && this.node.attributes.href){
18075                 this.fireEvent("click", this.node, e);
18076                 return;
18077             }
18078             e.preventDefault();
18079             if(this.disabled){
18080                 return;
18081             }
18082
18083             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18084                 this.node.toggle();
18085             }
18086
18087             this.fireEvent("click", this.node, e);
18088         }else{
18089             e.stopEvent();
18090         }
18091     },
18092
18093     onDblClick : function(e){
18094         e.preventDefault();
18095         if(this.disabled){
18096             return;
18097         }
18098         if(this.checkbox){
18099             this.toggleCheck();
18100         }
18101         if(!this.animating && this.node.hasChildNodes()){
18102             this.node.toggle();
18103         }
18104         this.fireEvent("dblclick", this.node, e);
18105     },
18106
18107     onCheckChange : function(){
18108         var checked = this.checkbox.checked;
18109         this.node.attributes.checked = checked;
18110         this.fireEvent('checkchange', this.node, checked);
18111     },
18112
18113     ecClick : function(e){
18114         if(!this.animating && this.node.hasChildNodes()){
18115             this.node.toggle();
18116         }
18117     },
18118
18119     startDrop : function(){
18120         this.dropping = true;
18121     },
18122
18123     // delayed drop so the click event doesn't get fired on a drop
18124     endDrop : function(){
18125        setTimeout(function(){
18126            this.dropping = false;
18127        }.createDelegate(this), 50);
18128     },
18129
18130     expand : function(){
18131         this.updateExpandIcon();
18132         this.ctNode.style.display = "";
18133     },
18134
18135     focus : function(){
18136         if(!this.node.preventHScroll){
18137             try{this.anchor.focus();
18138             }catch(e){}
18139         }else if(!Roo.isIE){
18140             try{
18141                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18142                 var l = noscroll.scrollLeft;
18143                 this.anchor.focus();
18144                 noscroll.scrollLeft = l;
18145             }catch(e){}
18146         }
18147     },
18148
18149     toggleCheck : function(value){
18150         var cb = this.checkbox;
18151         if(cb){
18152             cb.checked = (value === undefined ? !cb.checked : value);
18153         }
18154     },
18155
18156     blur : function(){
18157         try{
18158             this.anchor.blur();
18159         }catch(e){}
18160     },
18161
18162     animExpand : function(callback){
18163         var ct = Roo.get(this.ctNode);
18164         ct.stopFx();
18165         if(!this.node.hasChildNodes()){
18166             this.updateExpandIcon();
18167             this.ctNode.style.display = "";
18168             Roo.callback(callback);
18169             return;
18170         }
18171         this.animating = true;
18172         this.updateExpandIcon();
18173
18174         ct.slideIn('t', {
18175            callback : function(){
18176                this.animating = false;
18177                Roo.callback(callback);
18178             },
18179             scope: this,
18180             duration: this.node.ownerTree.duration || .25
18181         });
18182     },
18183
18184     highlight : function(){
18185         var tree = this.node.getOwnerTree();
18186         Roo.fly(this.wrap).highlight(
18187             tree.hlColor || "C3DAF9",
18188             {endColor: tree.hlBaseColor}
18189         );
18190     },
18191
18192     collapse : function(){
18193         this.updateExpandIcon();
18194         this.ctNode.style.display = "none";
18195     },
18196
18197     animCollapse : function(callback){
18198         var ct = Roo.get(this.ctNode);
18199         ct.enableDisplayMode('block');
18200         ct.stopFx();
18201
18202         this.animating = true;
18203         this.updateExpandIcon();
18204
18205         ct.slideOut('t', {
18206             callback : function(){
18207                this.animating = false;
18208                Roo.callback(callback);
18209             },
18210             scope: this,
18211             duration: this.node.ownerTree.duration || .25
18212         });
18213     },
18214
18215     getContainer : function(){
18216         return this.ctNode;
18217     },
18218
18219     getEl : function(){
18220         return this.wrap;
18221     },
18222
18223     appendDDGhost : function(ghostNode){
18224         ghostNode.appendChild(this.elNode.cloneNode(true));
18225     },
18226
18227     getDDRepairXY : function(){
18228         return Roo.lib.Dom.getXY(this.iconNode);
18229     },
18230
18231     onRender : function(){
18232         this.render();
18233     },
18234
18235     render : function(bulkRender){
18236         var n = this.node, a = n.attributes;
18237         var targetNode = n.parentNode ?
18238               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18239
18240         if(!this.rendered){
18241             this.rendered = true;
18242
18243             this.renderElements(n, a, targetNode, bulkRender);
18244
18245             if(a.qtip){
18246                if(this.textNode.setAttributeNS){
18247                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18248                    if(a.qtipTitle){
18249                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18250                    }
18251                }else{
18252                    this.textNode.setAttribute("ext:qtip", a.qtip);
18253                    if(a.qtipTitle){
18254                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18255                    }
18256                }
18257             }else if(a.qtipCfg){
18258                 a.qtipCfg.target = Roo.id(this.textNode);
18259                 Roo.QuickTips.register(a.qtipCfg);
18260             }
18261             this.initEvents();
18262             if(!this.node.expanded){
18263                 this.updateExpandIcon();
18264             }
18265         }else{
18266             if(bulkRender === true) {
18267                 targetNode.appendChild(this.wrap);
18268             }
18269         }
18270     },
18271
18272     renderElements : function(n, a, targetNode, bulkRender)
18273     {
18274         // add some indent caching, this helps performance when rendering a large tree
18275         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18276         var t = n.getOwnerTree();
18277         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18278         if (typeof(n.attributes.html) != 'undefined') {
18279             txt = n.attributes.html;
18280         }
18281         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18282         var cb = typeof a.checked == 'boolean';
18283         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18284         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18285             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18286             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18287             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18288             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18289             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18290              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18291                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18292             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18293             "</li>"];
18294
18295         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18296             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18297                                 n.nextSibling.ui.getEl(), buf.join(""));
18298         }else{
18299             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18300         }
18301
18302         this.elNode = this.wrap.childNodes[0];
18303         this.ctNode = this.wrap.childNodes[1];
18304         var cs = this.elNode.childNodes;
18305         this.indentNode = cs[0];
18306         this.ecNode = cs[1];
18307         this.iconNode = cs[2];
18308         var index = 3;
18309         if(cb){
18310             this.checkbox = cs[3];
18311             index++;
18312         }
18313         this.anchor = cs[index];
18314         this.textNode = cs[index].firstChild;
18315     },
18316
18317     getAnchor : function(){
18318         return this.anchor;
18319     },
18320
18321     getTextEl : function(){
18322         return this.textNode;
18323     },
18324
18325     getIconEl : function(){
18326         return this.iconNode;
18327     },
18328
18329     isChecked : function(){
18330         return this.checkbox ? this.checkbox.checked : false;
18331     },
18332
18333     updateExpandIcon : function(){
18334         if(this.rendered){
18335             var n = this.node, c1, c2;
18336             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18337             var hasChild = n.hasChildNodes();
18338             if(hasChild){
18339                 if(n.expanded){
18340                     cls += "-minus";
18341                     c1 = "x-tree-node-collapsed";
18342                     c2 = "x-tree-node-expanded";
18343                 }else{
18344                     cls += "-plus";
18345                     c1 = "x-tree-node-expanded";
18346                     c2 = "x-tree-node-collapsed";
18347                 }
18348                 if(this.wasLeaf){
18349                     this.removeClass("x-tree-node-leaf");
18350                     this.wasLeaf = false;
18351                 }
18352                 if(this.c1 != c1 || this.c2 != c2){
18353                     Roo.fly(this.elNode).replaceClass(c1, c2);
18354                     this.c1 = c1; this.c2 = c2;
18355                 }
18356             }else{
18357                 // this changes non-leafs into leafs if they have no children.
18358                 // it's not very rational behaviour..
18359                 
18360                 if(!this.wasLeaf && this.node.leaf){
18361                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18362                     delete this.c1;
18363                     delete this.c2;
18364                     this.wasLeaf = true;
18365                 }
18366             }
18367             var ecc = "x-tree-ec-icon "+cls;
18368             if(this.ecc != ecc){
18369                 this.ecNode.className = ecc;
18370                 this.ecc = ecc;
18371             }
18372         }
18373     },
18374
18375     getChildIndent : function(){
18376         if(!this.childIndent){
18377             var buf = [];
18378             var p = this.node;
18379             while(p){
18380                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18381                     if(!p.isLast()) {
18382                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18383                     } else {
18384                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18385                     }
18386                 }
18387                 p = p.parentNode;
18388             }
18389             this.childIndent = buf.join("");
18390         }
18391         return this.childIndent;
18392     },
18393
18394     renderIndent : function(){
18395         if(this.rendered){
18396             var indent = "";
18397             var p = this.node.parentNode;
18398             if(p){
18399                 indent = p.ui.getChildIndent();
18400             }
18401             if(this.indentMarkup != indent){ // don't rerender if not required
18402                 this.indentNode.innerHTML = indent;
18403                 this.indentMarkup = indent;
18404             }
18405             this.updateExpandIcon();
18406         }
18407     }
18408 };
18409
18410 Roo.tree.RootTreeNodeUI = function(){
18411     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18412 };
18413 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18414     render : function(){
18415         if(!this.rendered){
18416             var targetNode = this.node.ownerTree.innerCt.dom;
18417             this.node.expanded = true;
18418             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18419             this.wrap = this.ctNode = targetNode.firstChild;
18420         }
18421     },
18422     collapse : function(){
18423     },
18424     expand : function(){
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  * @class Roo.tree.TreeLoader
18438  * @extends Roo.util.Observable
18439  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18440  * nodes from a specified URL. The response must be a javascript Array definition
18441  * who's elements are node definition objects. eg:
18442  * <pre><code>
18443 {  success : true,
18444    data :      [
18445    
18446     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18447     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18448     ]
18449 }
18450
18451
18452 </code></pre>
18453  * <br><br>
18454  * The old style respose with just an array is still supported, but not recommended.
18455  * <br><br>
18456  *
18457  * A server request is sent, and child nodes are loaded only when a node is expanded.
18458  * The loading node's id is passed to the server under the parameter name "node" to
18459  * enable the server to produce the correct child nodes.
18460  * <br><br>
18461  * To pass extra parameters, an event handler may be attached to the "beforeload"
18462  * event, and the parameters specified in the TreeLoader's baseParams property:
18463  * <pre><code>
18464     myTreeLoader.on("beforeload", function(treeLoader, node) {
18465         this.baseParams.category = node.attributes.category;
18466     }, this);
18467 </code></pre><
18468  * This would pass an HTTP parameter called "category" to the server containing
18469  * the value of the Node's "category" attribute.
18470  * @constructor
18471  * Creates a new Treeloader.
18472  * @param {Object} config A config object containing config properties.
18473  */
18474 Roo.tree.TreeLoader = function(config){
18475     this.baseParams = {};
18476     this.requestMethod = "POST";
18477     Roo.apply(this, config);
18478
18479     this.addEvents({
18480     
18481         /**
18482          * @event beforeload
18483          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18484          * @param {Object} This TreeLoader object.
18485          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18486          * @param {Object} callback The callback function specified in the {@link #load} call.
18487          */
18488         beforeload : true,
18489         /**
18490          * @event load
18491          * Fires when the node has been successfuly loaded.
18492          * @param {Object} This TreeLoader object.
18493          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18494          * @param {Object} response The response object containing the data from the server.
18495          */
18496         load : true,
18497         /**
18498          * @event loadexception
18499          * Fires if the network request failed.
18500          * @param {Object} This TreeLoader object.
18501          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18502          * @param {Object} response The response object containing the data from the server.
18503          */
18504         loadexception : true,
18505         /**
18506          * @event create
18507          * Fires before a node is created, enabling you to return custom Node types 
18508          * @param {Object} This TreeLoader object.
18509          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18510          */
18511         create : true
18512     });
18513
18514     Roo.tree.TreeLoader.superclass.constructor.call(this);
18515 };
18516
18517 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18518     /**
18519     * @cfg {String} dataUrl The URL from which to request a Json string which
18520     * specifies an array of node definition object representing the child nodes
18521     * to be loaded.
18522     */
18523     /**
18524     * @cfg {String} requestMethod either GET or POST
18525     * defaults to POST (due to BC)
18526     * to be loaded.
18527     */
18528     /**
18529     * @cfg {Object} baseParams (optional) An object containing properties which
18530     * specify HTTP parameters to be passed to each request for child nodes.
18531     */
18532     /**
18533     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18534     * created by this loader. If the attributes sent by the server have an attribute in this object,
18535     * they take priority.
18536     */
18537     /**
18538     * @cfg {Object} uiProviders (optional) An object containing properties which
18539     * 
18540     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18541     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18542     * <i>uiProvider</i> attribute of a returned child node is a string rather
18543     * than a reference to a TreeNodeUI implementation, this that string value
18544     * is used as a property name in the uiProviders object. You can define the provider named
18545     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18546     */
18547     uiProviders : {},
18548
18549     /**
18550     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18551     * child nodes before loading.
18552     */
18553     clearOnLoad : true,
18554
18555     /**
18556     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18557     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18558     * Grid query { data : [ .....] }
18559     */
18560     
18561     root : false,
18562      /**
18563     * @cfg {String} queryParam (optional) 
18564     * Name of the query as it will be passed on the querystring (defaults to 'node')
18565     * eg. the request will be ?node=[id]
18566     */
18567     
18568     
18569     queryParam: false,
18570     
18571     /**
18572      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18573      * This is called automatically when a node is expanded, but may be used to reload
18574      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18575      * @param {Roo.tree.TreeNode} node
18576      * @param {Function} callback
18577      */
18578     load : function(node, callback){
18579         if(this.clearOnLoad){
18580             while(node.firstChild){
18581                 node.removeChild(node.firstChild);
18582             }
18583         }
18584         if(node.attributes.children){ // preloaded json children
18585             var cs = node.attributes.children;
18586             for(var i = 0, len = cs.length; i < len; i++){
18587                 node.appendChild(this.createNode(cs[i]));
18588             }
18589             if(typeof callback == "function"){
18590                 callback();
18591             }
18592         }else if(this.dataUrl){
18593             this.requestData(node, callback);
18594         }
18595     },
18596
18597     getParams: function(node){
18598         var buf = [], bp = this.baseParams;
18599         for(var key in bp){
18600             if(typeof bp[key] != "function"){
18601                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18602             }
18603         }
18604         var n = this.queryParam === false ? 'node' : this.queryParam;
18605         buf.push(n + "=", encodeURIComponent(node.id));
18606         return buf.join("");
18607     },
18608
18609     requestData : function(node, callback){
18610         if(this.fireEvent("beforeload", this, node, callback) !== false){
18611             this.transId = Roo.Ajax.request({
18612                 method:this.requestMethod,
18613                 url: this.dataUrl||this.url,
18614                 success: this.handleResponse,
18615                 failure: this.handleFailure,
18616                 scope: this,
18617                 argument: {callback: callback, node: node},
18618                 params: this.getParams(node)
18619             });
18620         }else{
18621             // if the load is cancelled, make sure we notify
18622             // the node that we are done
18623             if(typeof callback == "function"){
18624                 callback();
18625             }
18626         }
18627     },
18628
18629     isLoading : function(){
18630         return this.transId ? true : false;
18631     },
18632
18633     abort : function(){
18634         if(this.isLoading()){
18635             Roo.Ajax.abort(this.transId);
18636         }
18637     },
18638
18639     // private
18640     createNode : function(attr)
18641     {
18642         // apply baseAttrs, nice idea Corey!
18643         if(this.baseAttrs){
18644             Roo.applyIf(attr, this.baseAttrs);
18645         }
18646         if(this.applyLoader !== false){
18647             attr.loader = this;
18648         }
18649         // uiProvider = depreciated..
18650         
18651         if(typeof(attr.uiProvider) == 'string'){
18652            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18653                 /**  eval:var:attr */ eval(attr.uiProvider);
18654         }
18655         if(typeof(this.uiProviders['default']) != 'undefined') {
18656             attr.uiProvider = this.uiProviders['default'];
18657         }
18658         
18659         this.fireEvent('create', this, attr);
18660         
18661         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18662         return(attr.leaf ?
18663                         new Roo.tree.TreeNode(attr) :
18664                         new Roo.tree.AsyncTreeNode(attr));
18665     },
18666
18667     processResponse : function(response, node, callback)
18668     {
18669         var json = response.responseText;
18670         try {
18671             
18672             var o = Roo.decode(json);
18673             
18674             if (this.root === false && typeof(o.success) != undefined) {
18675                 this.root = 'data'; // the default behaviour for list like data..
18676                 }
18677                 
18678             if (this.root !== false &&  !o.success) {
18679                 // it's a failure condition.
18680                 var a = response.argument;
18681                 this.fireEvent("loadexception", this, a.node, response);
18682                 Roo.log("Load failed - should have a handler really");
18683                 return;
18684             }
18685             
18686             
18687             
18688             if (this.root !== false) {
18689                  o = o[this.root];
18690             }
18691             
18692             for(var i = 0, len = o.length; i < len; i++){
18693                 var n = this.createNode(o[i]);
18694                 if(n){
18695                     node.appendChild(n);
18696                 }
18697             }
18698             if(typeof callback == "function"){
18699                 callback(this, node);
18700             }
18701         }catch(e){
18702             this.handleFailure(response);
18703         }
18704     },
18705
18706     handleResponse : function(response){
18707         this.transId = false;
18708         var a = response.argument;
18709         this.processResponse(response, a.node, a.callback);
18710         this.fireEvent("load", this, a.node, response);
18711     },
18712
18713     handleFailure : function(response)
18714     {
18715         // should handle failure better..
18716         this.transId = false;
18717         var a = response.argument;
18718         this.fireEvent("loadexception", this, a.node, response);
18719         if(typeof a.callback == "function"){
18720             a.callback(this, a.node);
18721         }
18722     }
18723 });/*
18724  * Based on:
18725  * Ext JS Library 1.1.1
18726  * Copyright(c) 2006-2007, Ext JS, LLC.
18727  *
18728  * Originally Released Under LGPL - original licence link has changed is not relivant.
18729  *
18730  * Fork - LGPL
18731  * <script type="text/javascript">
18732  */
18733
18734 /**
18735 * @class Roo.tree.TreeFilter
18736 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18737 * @param {TreePanel} tree
18738 * @param {Object} config (optional)
18739  */
18740 Roo.tree.TreeFilter = function(tree, config){
18741     this.tree = tree;
18742     this.filtered = {};
18743     Roo.apply(this, config);
18744 };
18745
18746 Roo.tree.TreeFilter.prototype = {
18747     clearBlank:false,
18748     reverse:false,
18749     autoClear:false,
18750     remove:false,
18751
18752      /**
18753      * Filter the data by a specific attribute.
18754      * @param {String/RegExp} value Either string that the attribute value
18755      * should start with or a RegExp to test against the attribute
18756      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18757      * @param {TreeNode} startNode (optional) The node to start the filter at.
18758      */
18759     filter : function(value, attr, startNode){
18760         attr = attr || "text";
18761         var f;
18762         if(typeof value == "string"){
18763             var vlen = value.length;
18764             // auto clear empty filter
18765             if(vlen == 0 && this.clearBlank){
18766                 this.clear();
18767                 return;
18768             }
18769             value = value.toLowerCase();
18770             f = function(n){
18771                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18772             };
18773         }else if(value.exec){ // regex?
18774             f = function(n){
18775                 return value.test(n.attributes[attr]);
18776             };
18777         }else{
18778             throw 'Illegal filter type, must be string or regex';
18779         }
18780         this.filterBy(f, null, startNode);
18781         },
18782
18783     /**
18784      * Filter by a function. The passed function will be called with each
18785      * node in the tree (or from the startNode). If the function returns true, the node is kept
18786      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18787      * @param {Function} fn The filter function
18788      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18789      */
18790     filterBy : function(fn, scope, startNode){
18791         startNode = startNode || this.tree.root;
18792         if(this.autoClear){
18793             this.clear();
18794         }
18795         var af = this.filtered, rv = this.reverse;
18796         var f = function(n){
18797             if(n == startNode){
18798                 return true;
18799             }
18800             if(af[n.id]){
18801                 return false;
18802             }
18803             var m = fn.call(scope || n, n);
18804             if(!m || rv){
18805                 af[n.id] = n;
18806                 n.ui.hide();
18807                 return false;
18808             }
18809             return true;
18810         };
18811         startNode.cascade(f);
18812         if(this.remove){
18813            for(var id in af){
18814                if(typeof id != "function"){
18815                    var n = af[id];
18816                    if(n && n.parentNode){
18817                        n.parentNode.removeChild(n);
18818                    }
18819                }
18820            }
18821         }
18822     },
18823
18824     /**
18825      * Clears the current filter. Note: with the "remove" option
18826      * set a filter cannot be cleared.
18827      */
18828     clear : function(){
18829         var t = this.tree;
18830         var af = this.filtered;
18831         for(var id in af){
18832             if(typeof id != "function"){
18833                 var n = af[id];
18834                 if(n){
18835                     n.ui.show();
18836                 }
18837             }
18838         }
18839         this.filtered = {};
18840     }
18841 };
18842 /*
18843  * Based on:
18844  * Ext JS Library 1.1.1
18845  * Copyright(c) 2006-2007, Ext JS, LLC.
18846  *
18847  * Originally Released Under LGPL - original licence link has changed is not relivant.
18848  *
18849  * Fork - LGPL
18850  * <script type="text/javascript">
18851  */
18852  
18853
18854 /**
18855  * @class Roo.tree.TreeSorter
18856  * Provides sorting of nodes in a TreePanel
18857  * 
18858  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18859  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18860  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18861  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18862  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18863  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18864  * @constructor
18865  * @param {TreePanel} tree
18866  * @param {Object} config
18867  */
18868 Roo.tree.TreeSorter = function(tree, config){
18869     Roo.apply(this, config);
18870     tree.on("beforechildrenrendered", this.doSort, this);
18871     tree.on("append", this.updateSort, this);
18872     tree.on("insert", this.updateSort, this);
18873     
18874     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18875     var p = this.property || "text";
18876     var sortType = this.sortType;
18877     var fs = this.folderSort;
18878     var cs = this.caseSensitive === true;
18879     var leafAttr = this.leafAttr || 'leaf';
18880
18881     this.sortFn = function(n1, n2){
18882         if(fs){
18883             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18884                 return 1;
18885             }
18886             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18887                 return -1;
18888             }
18889         }
18890         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18891         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18892         if(v1 < v2){
18893                         return dsc ? +1 : -1;
18894                 }else if(v1 > v2){
18895                         return dsc ? -1 : +1;
18896         }else{
18897                 return 0;
18898         }
18899     };
18900 };
18901
18902 Roo.tree.TreeSorter.prototype = {
18903     doSort : function(node){
18904         node.sort(this.sortFn);
18905     },
18906     
18907     compareNodes : function(n1, n2){
18908         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18909     },
18910     
18911     updateSort : function(tree, node){
18912         if(node.childrenRendered){
18913             this.doSort.defer(1, this, [node]);
18914         }
18915     }
18916 };/*
18917  * Based on:
18918  * Ext JS Library 1.1.1
18919  * Copyright(c) 2006-2007, Ext JS, LLC.
18920  *
18921  * Originally Released Under LGPL - original licence link has changed is not relivant.
18922  *
18923  * Fork - LGPL
18924  * <script type="text/javascript">
18925  */
18926
18927 if(Roo.dd.DropZone){
18928     
18929 Roo.tree.TreeDropZone = function(tree, config){
18930     this.allowParentInsert = false;
18931     this.allowContainerDrop = false;
18932     this.appendOnly = false;
18933     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18934     this.tree = tree;
18935     this.lastInsertClass = "x-tree-no-status";
18936     this.dragOverData = {};
18937 };
18938
18939 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18940     ddGroup : "TreeDD",
18941     scroll:  true,
18942     
18943     expandDelay : 1000,
18944     
18945     expandNode : function(node){
18946         if(node.hasChildNodes() && !node.isExpanded()){
18947             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18948         }
18949     },
18950     
18951     queueExpand : function(node){
18952         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18953     },
18954     
18955     cancelExpand : function(){
18956         if(this.expandProcId){
18957             clearTimeout(this.expandProcId);
18958             this.expandProcId = false;
18959         }
18960     },
18961     
18962     isValidDropPoint : function(n, pt, dd, e, data){
18963         if(!n || !data){ return false; }
18964         var targetNode = n.node;
18965         var dropNode = data.node;
18966         // default drop rules
18967         if(!(targetNode && targetNode.isTarget && pt)){
18968             return false;
18969         }
18970         if(pt == "append" && targetNode.allowChildren === false){
18971             return false;
18972         }
18973         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18974             return false;
18975         }
18976         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18977             return false;
18978         }
18979         // reuse the object
18980         var overEvent = this.dragOverData;
18981         overEvent.tree = this.tree;
18982         overEvent.target = targetNode;
18983         overEvent.data = data;
18984         overEvent.point = pt;
18985         overEvent.source = dd;
18986         overEvent.rawEvent = e;
18987         overEvent.dropNode = dropNode;
18988         overEvent.cancel = false;  
18989         var result = this.tree.fireEvent("nodedragover", overEvent);
18990         return overEvent.cancel === false && result !== false;
18991     },
18992     
18993     getDropPoint : function(e, n, dd)
18994     {
18995         var tn = n.node;
18996         if(tn.isRoot){
18997             return tn.allowChildren !== false ? "append" : false; // always append for root
18998         }
18999         var dragEl = n.ddel;
19000         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
19001         var y = Roo.lib.Event.getPageY(e);
19002         //var noAppend = tn.allowChildren === false || tn.isLeaf();
19003         
19004         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
19005         var noAppend = tn.allowChildren === false;
19006         if(this.appendOnly || tn.parentNode.allowChildren === false){
19007             return noAppend ? false : "append";
19008         }
19009         var noBelow = false;
19010         if(!this.allowParentInsert){
19011             noBelow = tn.hasChildNodes() && tn.isExpanded();
19012         }
19013         var q = (b - t) / (noAppend ? 2 : 3);
19014         if(y >= t && y < (t + q)){
19015             return "above";
19016         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19017             return "below";
19018         }else{
19019             return "append";
19020         }
19021     },
19022     
19023     onNodeEnter : function(n, dd, e, data)
19024     {
19025         this.cancelExpand();
19026     },
19027     
19028     onNodeOver : function(n, dd, e, data)
19029     {
19030        
19031         var pt = this.getDropPoint(e, n, dd);
19032         var node = n.node;
19033         
19034         // auto node expand check
19035         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19036             this.queueExpand(node);
19037         }else if(pt != "append"){
19038             this.cancelExpand();
19039         }
19040         
19041         // set the insert point style on the target node
19042         var returnCls = this.dropNotAllowed;
19043         if(this.isValidDropPoint(n, pt, dd, e, data)){
19044            if(pt){
19045                var el = n.ddel;
19046                var cls;
19047                if(pt == "above"){
19048                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19049                    cls = "x-tree-drag-insert-above";
19050                }else if(pt == "below"){
19051                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19052                    cls = "x-tree-drag-insert-below";
19053                }else{
19054                    returnCls = "x-tree-drop-ok-append";
19055                    cls = "x-tree-drag-append";
19056                }
19057                if(this.lastInsertClass != cls){
19058                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19059                    this.lastInsertClass = cls;
19060                }
19061            }
19062        }
19063        return returnCls;
19064     },
19065     
19066     onNodeOut : function(n, dd, e, data){
19067         
19068         this.cancelExpand();
19069         this.removeDropIndicators(n);
19070     },
19071     
19072     onNodeDrop : function(n, dd, e, data){
19073         var point = this.getDropPoint(e, n, dd);
19074         var targetNode = n.node;
19075         targetNode.ui.startDrop();
19076         if(!this.isValidDropPoint(n, point, dd, e, data)){
19077             targetNode.ui.endDrop();
19078             return false;
19079         }
19080         // first try to find the drop node
19081         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19082         var dropEvent = {
19083             tree : this.tree,
19084             target: targetNode,
19085             data: data,
19086             point: point,
19087             source: dd,
19088             rawEvent: e,
19089             dropNode: dropNode,
19090             cancel: !dropNode   
19091         };
19092         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19093         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19094             targetNode.ui.endDrop();
19095             return false;
19096         }
19097         // allow target changing
19098         targetNode = dropEvent.target;
19099         if(point == "append" && !targetNode.isExpanded()){
19100             targetNode.expand(false, null, function(){
19101                 this.completeDrop(dropEvent);
19102             }.createDelegate(this));
19103         }else{
19104             this.completeDrop(dropEvent);
19105         }
19106         return true;
19107     },
19108     
19109     completeDrop : function(de){
19110         var ns = de.dropNode, p = de.point, t = de.target;
19111         if(!(ns instanceof Array)){
19112             ns = [ns];
19113         }
19114         var n;
19115         for(var i = 0, len = ns.length; i < len; i++){
19116             n = ns[i];
19117             if(p == "above"){
19118                 t.parentNode.insertBefore(n, t);
19119             }else if(p == "below"){
19120                 t.parentNode.insertBefore(n, t.nextSibling);
19121             }else{
19122                 t.appendChild(n);
19123             }
19124         }
19125         n.ui.focus();
19126         if(this.tree.hlDrop){
19127             n.ui.highlight();
19128         }
19129         t.ui.endDrop();
19130         this.tree.fireEvent("nodedrop", de);
19131     },
19132     
19133     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19134         if(this.tree.hlDrop){
19135             dropNode.ui.focus();
19136             dropNode.ui.highlight();
19137         }
19138         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19139     },
19140     
19141     getTree : function(){
19142         return this.tree;
19143     },
19144     
19145     removeDropIndicators : function(n){
19146         if(n && n.ddel){
19147             var el = n.ddel;
19148             Roo.fly(el).removeClass([
19149                     "x-tree-drag-insert-above",
19150                     "x-tree-drag-insert-below",
19151                     "x-tree-drag-append"]);
19152             this.lastInsertClass = "_noclass";
19153         }
19154     },
19155     
19156     beforeDragDrop : function(target, e, id){
19157         this.cancelExpand();
19158         return true;
19159     },
19160     
19161     afterRepair : function(data){
19162         if(data && Roo.enableFx){
19163             data.node.ui.highlight();
19164         }
19165         this.hideProxy();
19166     } 
19167     
19168 });
19169
19170 }
19171 /*
19172  * Based on:
19173  * Ext JS Library 1.1.1
19174  * Copyright(c) 2006-2007, Ext JS, LLC.
19175  *
19176  * Originally Released Under LGPL - original licence link has changed is not relivant.
19177  *
19178  * Fork - LGPL
19179  * <script type="text/javascript">
19180  */
19181  
19182
19183 if(Roo.dd.DragZone){
19184 Roo.tree.TreeDragZone = function(tree, config){
19185     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19186     this.tree = tree;
19187 };
19188
19189 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19190     ddGroup : "TreeDD",
19191    
19192     onBeforeDrag : function(data, e){
19193         var n = data.node;
19194         return n && n.draggable && !n.disabled;
19195     },
19196      
19197     
19198     onInitDrag : function(e){
19199         var data = this.dragData;
19200         this.tree.getSelectionModel().select(data.node);
19201         this.proxy.update("");
19202         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19203         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19204     },
19205     
19206     getRepairXY : function(e, data){
19207         return data.node.ui.getDDRepairXY();
19208     },
19209     
19210     onEndDrag : function(data, e){
19211         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19212         
19213         
19214     },
19215     
19216     onValidDrop : function(dd, e, id){
19217         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19218         this.hideProxy();
19219     },
19220     
19221     beforeInvalidDrop : function(e, id){
19222         // this scrolls the original position back into view
19223         var sm = this.tree.getSelectionModel();
19224         sm.clearSelections();
19225         sm.select(this.dragData.node);
19226     }
19227 });
19228 }/*
19229  * Based on:
19230  * Ext JS Library 1.1.1
19231  * Copyright(c) 2006-2007, Ext JS, LLC.
19232  *
19233  * Originally Released Under LGPL - original licence link has changed is not relivant.
19234  *
19235  * Fork - LGPL
19236  * <script type="text/javascript">
19237  */
19238 /**
19239  * @class Roo.tree.TreeEditor
19240  * @extends Roo.Editor
19241  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19242  * as the editor field.
19243  * @constructor
19244  * @param {Object} config (used to be the tree panel.)
19245  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19246  * 
19247  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19248  * @cfg {Roo.form.TextField|Object} field The field configuration
19249  *
19250  * 
19251  */
19252 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19253     var tree = config;
19254     var field;
19255     if (oldconfig) { // old style..
19256         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19257     } else {
19258         // new style..
19259         tree = config.tree;
19260         config.field = config.field  || {};
19261         config.field.xtype = 'TextField';
19262         field = Roo.factory(config.field, Roo.form);
19263     }
19264     config = config || {};
19265     
19266     
19267     this.addEvents({
19268         /**
19269          * @event beforenodeedit
19270          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19271          * false from the handler of this event.
19272          * @param {Editor} this
19273          * @param {Roo.tree.Node} node 
19274          */
19275         "beforenodeedit" : true
19276     });
19277     
19278     //Roo.log(config);
19279     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19280
19281     this.tree = tree;
19282
19283     tree.on('beforeclick', this.beforeNodeClick, this);
19284     tree.getTreeEl().on('mousedown', this.hide, this);
19285     this.on('complete', this.updateNode, this);
19286     this.on('beforestartedit', this.fitToTree, this);
19287     this.on('startedit', this.bindScroll, this, {delay:10});
19288     this.on('specialkey', this.onSpecialKey, this);
19289 };
19290
19291 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19292     /**
19293      * @cfg {String} alignment
19294      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19295      */
19296     alignment: "l-l",
19297     // inherit
19298     autoSize: false,
19299     /**
19300      * @cfg {Boolean} hideEl
19301      * True to hide the bound element while the editor is displayed (defaults to false)
19302      */
19303     hideEl : false,
19304     /**
19305      * @cfg {String} cls
19306      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19307      */
19308     cls: "x-small-editor x-tree-editor",
19309     /**
19310      * @cfg {Boolean} shim
19311      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19312      */
19313     shim:false,
19314     // inherit
19315     shadow:"frame",
19316     /**
19317      * @cfg {Number} maxWidth
19318      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19319      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19320      * scroll and client offsets into account prior to each edit.
19321      */
19322     maxWidth: 250,
19323
19324     editDelay : 350,
19325
19326     // private
19327     fitToTree : function(ed, el){
19328         var td = this.tree.getTreeEl().dom, nd = el.dom;
19329         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19330             td.scrollLeft = nd.offsetLeft;
19331         }
19332         var w = Math.min(
19333                 this.maxWidth,
19334                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19335         this.setSize(w, '');
19336         
19337         return this.fireEvent('beforenodeedit', this, this.editNode);
19338         
19339     },
19340
19341     // private
19342     triggerEdit : function(node){
19343         this.completeEdit();
19344         this.editNode = node;
19345         this.startEdit(node.ui.textNode, node.text);
19346     },
19347
19348     // private
19349     bindScroll : function(){
19350         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19351     },
19352
19353     // private
19354     beforeNodeClick : function(node, e){
19355         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19356         this.lastClick = new Date();
19357         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19358             e.stopEvent();
19359             this.triggerEdit(node);
19360             return false;
19361         }
19362         return true;
19363     },
19364
19365     // private
19366     updateNode : function(ed, value){
19367         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19368         this.editNode.setText(value);
19369     },
19370
19371     // private
19372     onHide : function(){
19373         Roo.tree.TreeEditor.superclass.onHide.call(this);
19374         if(this.editNode){
19375             this.editNode.ui.focus();
19376         }
19377     },
19378
19379     // private
19380     onSpecialKey : function(field, e){
19381         var k = e.getKey();
19382         if(k == e.ESC){
19383             e.stopEvent();
19384             this.cancelEdit();
19385         }else if(k == e.ENTER && !e.hasModifier()){
19386             e.stopEvent();
19387             this.completeEdit();
19388         }
19389     }
19390 });//<Script type="text/javascript">
19391 /*
19392  * Based on:
19393  * Ext JS Library 1.1.1
19394  * Copyright(c) 2006-2007, Ext JS, LLC.
19395  *
19396  * Originally Released Under LGPL - original licence link has changed is not relivant.
19397  *
19398  * Fork - LGPL
19399  * <script type="text/javascript">
19400  */
19401  
19402 /**
19403  * Not documented??? - probably should be...
19404  */
19405
19406 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19407     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19408     
19409     renderElements : function(n, a, targetNode, bulkRender){
19410         //consel.log("renderElements?");
19411         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19412
19413         var t = n.getOwnerTree();
19414         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19415         
19416         var cols = t.columns;
19417         var bw = t.borderWidth;
19418         var c = cols[0];
19419         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19420          var cb = typeof a.checked == "boolean";
19421         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19422         var colcls = 'x-t-' + tid + '-c0';
19423         var buf = [
19424             '<li class="x-tree-node">',
19425             
19426                 
19427                 '<div class="x-tree-node-el ', a.cls,'">',
19428                     // extran...
19429                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19430                 
19431                 
19432                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19433                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19434                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19435                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19436                            (a.iconCls ? ' '+a.iconCls : ''),
19437                            '" unselectable="on" />',
19438                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19439                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19440                              
19441                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19442                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19443                             '<span unselectable="on" qtip="' + tx + '">',
19444                              tx,
19445                              '</span></a>' ,
19446                     '</div>',
19447                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19448                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19449                  ];
19450         for(var i = 1, len = cols.length; i < len; i++){
19451             c = cols[i];
19452             colcls = 'x-t-' + tid + '-c' +i;
19453             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19454             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19455                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19456                       "</div>");
19457          }
19458          
19459          buf.push(
19460             '</a>',
19461             '<div class="x-clear"></div></div>',
19462             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19463             "</li>");
19464         
19465         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19466             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19467                                 n.nextSibling.ui.getEl(), buf.join(""));
19468         }else{
19469             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19470         }
19471         var el = this.wrap.firstChild;
19472         this.elRow = el;
19473         this.elNode = el.firstChild;
19474         this.ranchor = el.childNodes[1];
19475         this.ctNode = this.wrap.childNodes[1];
19476         var cs = el.firstChild.childNodes;
19477         this.indentNode = cs[0];
19478         this.ecNode = cs[1];
19479         this.iconNode = cs[2];
19480         var index = 3;
19481         if(cb){
19482             this.checkbox = cs[3];
19483             index++;
19484         }
19485         this.anchor = cs[index];
19486         
19487         this.textNode = cs[index].firstChild;
19488         
19489         //el.on("click", this.onClick, this);
19490         //el.on("dblclick", this.onDblClick, this);
19491         
19492         
19493        // console.log(this);
19494     },
19495     initEvents : function(){
19496         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19497         
19498             
19499         var a = this.ranchor;
19500
19501         var el = Roo.get(a);
19502
19503         if(Roo.isOpera){ // opera render bug ignores the CSS
19504             el.setStyle("text-decoration", "none");
19505         }
19506
19507         el.on("click", this.onClick, this);
19508         el.on("dblclick", this.onDblClick, this);
19509         el.on("contextmenu", this.onContextMenu, this);
19510         
19511     },
19512     
19513     /*onSelectedChange : function(state){
19514         if(state){
19515             this.focus();
19516             this.addClass("x-tree-selected");
19517         }else{
19518             //this.blur();
19519             this.removeClass("x-tree-selected");
19520         }
19521     },*/
19522     addClass : function(cls){
19523         if(this.elRow){
19524             Roo.fly(this.elRow).addClass(cls);
19525         }
19526         
19527     },
19528     
19529     
19530     removeClass : function(cls){
19531         if(this.elRow){
19532             Roo.fly(this.elRow).removeClass(cls);
19533         }
19534     }
19535
19536     
19537     
19538 });//<Script type="text/javascript">
19539
19540 /*
19541  * Based on:
19542  * Ext JS Library 1.1.1
19543  * Copyright(c) 2006-2007, Ext JS, LLC.
19544  *
19545  * Originally Released Under LGPL - original licence link has changed is not relivant.
19546  *
19547  * Fork - LGPL
19548  * <script type="text/javascript">
19549  */
19550  
19551
19552 /**
19553  * @class Roo.tree.ColumnTree
19554  * @extends Roo.data.TreePanel
19555  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19556  * @cfg {int} borderWidth  compined right/left border allowance
19557  * @constructor
19558  * @param {String/HTMLElement/Element} el The container element
19559  * @param {Object} config
19560  */
19561 Roo.tree.ColumnTree =  function(el, config)
19562 {
19563    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19564    this.addEvents({
19565         /**
19566         * @event resize
19567         * Fire this event on a container when it resizes
19568         * @param {int} w Width
19569         * @param {int} h Height
19570         */
19571        "resize" : true
19572     });
19573     this.on('resize', this.onResize, this);
19574 };
19575
19576 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19577     //lines:false,
19578     
19579     
19580     borderWidth: Roo.isBorderBox ? 0 : 2, 
19581     headEls : false,
19582     
19583     render : function(){
19584         // add the header.....
19585        
19586         Roo.tree.ColumnTree.superclass.render.apply(this);
19587         
19588         this.el.addClass('x-column-tree');
19589         
19590         this.headers = this.el.createChild(
19591             {cls:'x-tree-headers'},this.innerCt.dom);
19592    
19593         var cols = this.columns, c;
19594         var totalWidth = 0;
19595         this.headEls = [];
19596         var  len = cols.length;
19597         for(var i = 0; i < len; i++){
19598              c = cols[i];
19599              totalWidth += c.width;
19600             this.headEls.push(this.headers.createChild({
19601                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19602                  cn: {
19603                      cls:'x-tree-hd-text',
19604                      html: c.header
19605                  },
19606                  style:'width:'+(c.width-this.borderWidth)+'px;'
19607              }));
19608         }
19609         this.headers.createChild({cls:'x-clear'});
19610         // prevent floats from wrapping when clipped
19611         this.headers.setWidth(totalWidth);
19612         //this.innerCt.setWidth(totalWidth);
19613         this.innerCt.setStyle({ overflow: 'auto' });
19614         this.onResize(this.width, this.height);
19615              
19616         
19617     },
19618     onResize : function(w,h)
19619     {
19620         this.height = h;
19621         this.width = w;
19622         // resize cols..
19623         this.innerCt.setWidth(this.width);
19624         this.innerCt.setHeight(this.height-20);
19625         
19626         // headers...
19627         var cols = this.columns, c;
19628         var totalWidth = 0;
19629         var expEl = false;
19630         var len = cols.length;
19631         for(var i = 0; i < len; i++){
19632             c = cols[i];
19633             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19634                 // it's the expander..
19635                 expEl  = this.headEls[i];
19636                 continue;
19637             }
19638             totalWidth += c.width;
19639             
19640         }
19641         if (expEl) {
19642             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19643         }
19644         this.headers.setWidth(w-20);
19645
19646         
19647         
19648         
19649     }
19650 });
19651 /*
19652  * Based on:
19653  * Ext JS Library 1.1.1
19654  * Copyright(c) 2006-2007, Ext JS, LLC.
19655  *
19656  * Originally Released Under LGPL - original licence link has changed is not relivant.
19657  *
19658  * Fork - LGPL
19659  * <script type="text/javascript">
19660  */
19661  
19662 /**
19663  * @class Roo.menu.Menu
19664  * @extends Roo.util.Observable
19665  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19666  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19667  * @constructor
19668  * Creates a new Menu
19669  * @param {Object} config Configuration options
19670  */
19671 Roo.menu.Menu = function(config){
19672     Roo.apply(this, config);
19673     this.id = this.id || Roo.id();
19674     this.addEvents({
19675         /**
19676          * @event beforeshow
19677          * Fires before this menu is displayed
19678          * @param {Roo.menu.Menu} this
19679          */
19680         beforeshow : true,
19681         /**
19682          * @event beforehide
19683          * Fires before this menu is hidden
19684          * @param {Roo.menu.Menu} this
19685          */
19686         beforehide : true,
19687         /**
19688          * @event show
19689          * Fires after this menu is displayed
19690          * @param {Roo.menu.Menu} this
19691          */
19692         show : true,
19693         /**
19694          * @event hide
19695          * Fires after this menu is hidden
19696          * @param {Roo.menu.Menu} this
19697          */
19698         hide : true,
19699         /**
19700          * @event click
19701          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19702          * @param {Roo.menu.Menu} this
19703          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19704          * @param {Roo.EventObject} e
19705          */
19706         click : true,
19707         /**
19708          * @event mouseover
19709          * Fires when the mouse is hovering over this menu
19710          * @param {Roo.menu.Menu} this
19711          * @param {Roo.EventObject} e
19712          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19713          */
19714         mouseover : true,
19715         /**
19716          * @event mouseout
19717          * Fires when the mouse exits this menu
19718          * @param {Roo.menu.Menu} this
19719          * @param {Roo.EventObject} e
19720          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19721          */
19722         mouseout : true,
19723         /**
19724          * @event itemclick
19725          * Fires when a menu item contained in this menu is clicked
19726          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19727          * @param {Roo.EventObject} e
19728          */
19729         itemclick: true
19730     });
19731     if (this.registerMenu) {
19732         Roo.menu.MenuMgr.register(this);
19733     }
19734     
19735     var mis = this.items;
19736     this.items = new Roo.util.MixedCollection();
19737     if(mis){
19738         this.add.apply(this, mis);
19739     }
19740 };
19741
19742 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19743     /**
19744      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19745      */
19746     minWidth : 120,
19747     /**
19748      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19749      * for bottom-right shadow (defaults to "sides")
19750      */
19751     shadow : "sides",
19752     /**
19753      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19754      * this menu (defaults to "tl-tr?")
19755      */
19756     subMenuAlign : "tl-tr?",
19757     /**
19758      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19759      * relative to its element of origin (defaults to "tl-bl?")
19760      */
19761     defaultAlign : "tl-bl?",
19762     /**
19763      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19764      */
19765     allowOtherMenus : false,
19766     /**
19767      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19768      */
19769     registerMenu : true,
19770
19771     hidden:true,
19772
19773     // private
19774     render : function(){
19775         if(this.el){
19776             return;
19777         }
19778         var el = this.el = new Roo.Layer({
19779             cls: "x-menu",
19780             shadow:this.shadow,
19781             constrain: false,
19782             parentEl: this.parentEl || document.body,
19783             zindex:15000
19784         });
19785
19786         this.keyNav = new Roo.menu.MenuNav(this);
19787
19788         if(this.plain){
19789             el.addClass("x-menu-plain");
19790         }
19791         if(this.cls){
19792             el.addClass(this.cls);
19793         }
19794         // generic focus element
19795         this.focusEl = el.createChild({
19796             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19797         });
19798         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19799         ul.on("click", this.onClick, this);
19800         ul.on("mouseover", this.onMouseOver, this);
19801         ul.on("mouseout", this.onMouseOut, this);
19802         this.items.each(function(item){
19803             var li = document.createElement("li");
19804             li.className = "x-menu-list-item";
19805             ul.dom.appendChild(li);
19806             item.render(li, this);
19807         }, this);
19808         this.ul = ul;
19809         this.autoWidth();
19810     },
19811
19812     // private
19813     autoWidth : function(){
19814         var el = this.el, ul = this.ul;
19815         if(!el){
19816             return;
19817         }
19818         var w = this.width;
19819         if(w){
19820             el.setWidth(w);
19821         }else if(Roo.isIE){
19822             el.setWidth(this.minWidth);
19823             var t = el.dom.offsetWidth; // force recalc
19824             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19825         }
19826     },
19827
19828     // private
19829     delayAutoWidth : function(){
19830         if(this.rendered){
19831             if(!this.awTask){
19832                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19833             }
19834             this.awTask.delay(20);
19835         }
19836     },
19837
19838     // private
19839     findTargetItem : function(e){
19840         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19841         if(t && t.menuItemId){
19842             return this.items.get(t.menuItemId);
19843         }
19844     },
19845
19846     // private
19847     onClick : function(e){
19848         var t;
19849         if(t = this.findTargetItem(e)){
19850             t.onClick(e);
19851             this.fireEvent("click", this, t, e);
19852         }
19853     },
19854
19855     // private
19856     setActiveItem : function(item, autoExpand){
19857         if(item != this.activeItem){
19858             if(this.activeItem){
19859                 this.activeItem.deactivate();
19860             }
19861             this.activeItem = item;
19862             item.activate(autoExpand);
19863         }else if(autoExpand){
19864             item.expandMenu();
19865         }
19866     },
19867
19868     // private
19869     tryActivate : function(start, step){
19870         var items = this.items;
19871         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19872             var item = items.get(i);
19873             if(!item.disabled && item.canActivate){
19874                 this.setActiveItem(item, false);
19875                 return item;
19876             }
19877         }
19878         return false;
19879     },
19880
19881     // private
19882     onMouseOver : function(e){
19883         var t;
19884         if(t = this.findTargetItem(e)){
19885             if(t.canActivate && !t.disabled){
19886                 this.setActiveItem(t, true);
19887             }
19888         }
19889         this.fireEvent("mouseover", this, e, t);
19890     },
19891
19892     // private
19893     onMouseOut : function(e){
19894         var t;
19895         if(t = this.findTargetItem(e)){
19896             if(t == this.activeItem && t.shouldDeactivate(e)){
19897                 this.activeItem.deactivate();
19898                 delete this.activeItem;
19899             }
19900         }
19901         this.fireEvent("mouseout", this, e, t);
19902     },
19903
19904     /**
19905      * Read-only.  Returns true if the menu is currently displayed, else false.
19906      * @type Boolean
19907      */
19908     isVisible : function(){
19909         return this.el && !this.hidden;
19910     },
19911
19912     /**
19913      * Displays this menu relative to another element
19914      * @param {String/HTMLElement/Roo.Element} element The element to align to
19915      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19916      * the element (defaults to this.defaultAlign)
19917      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19918      */
19919     show : function(el, pos, parentMenu){
19920         this.parentMenu = parentMenu;
19921         if(!this.el){
19922             this.render();
19923         }
19924         this.fireEvent("beforeshow", this);
19925         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19926     },
19927
19928     /**
19929      * Displays this menu at a specific xy position
19930      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19931      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19932      */
19933     showAt : function(xy, parentMenu, /* private: */_e){
19934         this.parentMenu = parentMenu;
19935         if(!this.el){
19936             this.render();
19937         }
19938         if(_e !== false){
19939             this.fireEvent("beforeshow", this);
19940             xy = this.el.adjustForConstraints(xy);
19941         }
19942         this.el.setXY(xy);
19943         this.el.show();
19944         this.hidden = false;
19945         this.focus();
19946         this.fireEvent("show", this);
19947     },
19948
19949     focus : function(){
19950         if(!this.hidden){
19951             this.doFocus.defer(50, this);
19952         }
19953     },
19954
19955     doFocus : function(){
19956         if(!this.hidden){
19957             this.focusEl.focus();
19958         }
19959     },
19960
19961     /**
19962      * Hides this menu and optionally all parent menus
19963      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19964      */
19965     hide : function(deep){
19966         if(this.el && this.isVisible()){
19967             this.fireEvent("beforehide", this);
19968             if(this.activeItem){
19969                 this.activeItem.deactivate();
19970                 this.activeItem = null;
19971             }
19972             this.el.hide();
19973             this.hidden = true;
19974             this.fireEvent("hide", this);
19975         }
19976         if(deep === true && this.parentMenu){
19977             this.parentMenu.hide(true);
19978         }
19979     },
19980
19981     /**
19982      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19983      * Any of the following are valid:
19984      * <ul>
19985      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19986      * <li>An HTMLElement object which will be converted to a menu item</li>
19987      * <li>A menu item config object that will be created as a new menu item</li>
19988      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19989      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19990      * </ul>
19991      * Usage:
19992      * <pre><code>
19993 // Create the menu
19994 var menu = new Roo.menu.Menu();
19995
19996 // Create a menu item to add by reference
19997 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19998
19999 // Add a bunch of items at once using different methods.
20000 // Only the last item added will be returned.
20001 var item = menu.add(
20002     menuItem,                // add existing item by ref
20003     'Dynamic Item',          // new TextItem
20004     '-',                     // new separator
20005     { text: 'Config Item' }  // new item by config
20006 );
20007 </code></pre>
20008      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
20009      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
20010      */
20011     add : function(){
20012         var a = arguments, l = a.length, item;
20013         for(var i = 0; i < l; i++){
20014             var el = a[i];
20015             if ((typeof(el) == "object") && el.xtype && el.xns) {
20016                 el = Roo.factory(el, Roo.menu);
20017             }
20018             
20019             if(el.render){ // some kind of Item
20020                 item = this.addItem(el);
20021             }else if(typeof el == "string"){ // string
20022                 if(el == "separator" || el == "-"){
20023                     item = this.addSeparator();
20024                 }else{
20025                     item = this.addText(el);
20026                 }
20027             }else if(el.tagName || el.el){ // element
20028                 item = this.addElement(el);
20029             }else if(typeof el == "object"){ // must be menu item config?
20030                 item = this.addMenuItem(el);
20031             }
20032         }
20033         return item;
20034     },
20035
20036     /**
20037      * Returns this menu's underlying {@link Roo.Element} object
20038      * @return {Roo.Element} The element
20039      */
20040     getEl : function(){
20041         if(!this.el){
20042             this.render();
20043         }
20044         return this.el;
20045     },
20046
20047     /**
20048      * Adds a separator bar to the menu
20049      * @return {Roo.menu.Item} The menu item that was added
20050      */
20051     addSeparator : function(){
20052         return this.addItem(new Roo.menu.Separator());
20053     },
20054
20055     /**
20056      * Adds an {@link Roo.Element} object to the menu
20057      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20058      * @return {Roo.menu.Item} The menu item that was added
20059      */
20060     addElement : function(el){
20061         return this.addItem(new Roo.menu.BaseItem(el));
20062     },
20063
20064     /**
20065      * Adds an existing object based on {@link Roo.menu.Item} to the menu
20066      * @param {Roo.menu.Item} item The menu item to add
20067      * @return {Roo.menu.Item} The menu item that was added
20068      */
20069     addItem : function(item){
20070         this.items.add(item);
20071         if(this.ul){
20072             var li = document.createElement("li");
20073             li.className = "x-menu-list-item";
20074             this.ul.dom.appendChild(li);
20075             item.render(li, this);
20076             this.delayAutoWidth();
20077         }
20078         return item;
20079     },
20080
20081     /**
20082      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20083      * @param {Object} config A MenuItem config object
20084      * @return {Roo.menu.Item} The menu item that was added
20085      */
20086     addMenuItem : function(config){
20087         if(!(config instanceof Roo.menu.Item)){
20088             if(typeof config.checked == "boolean"){ // must be check menu item config?
20089                 config = new Roo.menu.CheckItem(config);
20090             }else{
20091                 config = new Roo.menu.Item(config);
20092             }
20093         }
20094         return this.addItem(config);
20095     },
20096
20097     /**
20098      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20099      * @param {String} text The text to display in the menu item
20100      * @return {Roo.menu.Item} The menu item that was added
20101      */
20102     addText : function(text){
20103         return this.addItem(new Roo.menu.TextItem({ text : text }));
20104     },
20105
20106     /**
20107      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20108      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20109      * @param {Roo.menu.Item} item The menu item to add
20110      * @return {Roo.menu.Item} The menu item that was added
20111      */
20112     insert : function(index, item){
20113         this.items.insert(index, item);
20114         if(this.ul){
20115             var li = document.createElement("li");
20116             li.className = "x-menu-list-item";
20117             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20118             item.render(li, this);
20119             this.delayAutoWidth();
20120         }
20121         return item;
20122     },
20123
20124     /**
20125      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20126      * @param {Roo.menu.Item} item The menu item to remove
20127      */
20128     remove : function(item){
20129         this.items.removeKey(item.id);
20130         item.destroy();
20131     },
20132
20133     /**
20134      * Removes and destroys all items in the menu
20135      */
20136     removeAll : function(){
20137         var f;
20138         while(f = this.items.first()){
20139             this.remove(f);
20140         }
20141     }
20142 });
20143
20144 // MenuNav is a private utility class used internally by the Menu
20145 Roo.menu.MenuNav = function(menu){
20146     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20147     this.scope = this.menu = menu;
20148 };
20149
20150 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20151     doRelay : function(e, h){
20152         var k = e.getKey();
20153         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20154             this.menu.tryActivate(0, 1);
20155             return false;
20156         }
20157         return h.call(this.scope || this, e, this.menu);
20158     },
20159
20160     up : function(e, m){
20161         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20162             m.tryActivate(m.items.length-1, -1);
20163         }
20164     },
20165
20166     down : function(e, m){
20167         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20168             m.tryActivate(0, 1);
20169         }
20170     },
20171
20172     right : function(e, m){
20173         if(m.activeItem){
20174             m.activeItem.expandMenu(true);
20175         }
20176     },
20177
20178     left : function(e, m){
20179         m.hide();
20180         if(m.parentMenu && m.parentMenu.activeItem){
20181             m.parentMenu.activeItem.activate();
20182         }
20183     },
20184
20185     enter : function(e, m){
20186         if(m.activeItem){
20187             e.stopPropagation();
20188             m.activeItem.onClick(e);
20189             m.fireEvent("click", this, m.activeItem);
20190             return true;
20191         }
20192     }
20193 });/*
20194  * Based on:
20195  * Ext JS Library 1.1.1
20196  * Copyright(c) 2006-2007, Ext JS, LLC.
20197  *
20198  * Originally Released Under LGPL - original licence link has changed is not relivant.
20199  *
20200  * Fork - LGPL
20201  * <script type="text/javascript">
20202  */
20203  
20204 /**
20205  * @class Roo.menu.MenuMgr
20206  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20207  * @singleton
20208  */
20209 Roo.menu.MenuMgr = function(){
20210    var menus, active, groups = {}, attached = false, lastShow = new Date();
20211
20212    // private - called when first menu is created
20213    function init(){
20214        menus = {};
20215        active = new Roo.util.MixedCollection();
20216        Roo.get(document).addKeyListener(27, function(){
20217            if(active.length > 0){
20218                hideAll();
20219            }
20220        });
20221    }
20222
20223    // private
20224    function hideAll(){
20225        if(active && active.length > 0){
20226            var c = active.clone();
20227            c.each(function(m){
20228                m.hide();
20229            });
20230        }
20231    }
20232
20233    // private
20234    function onHide(m){
20235        active.remove(m);
20236        if(active.length < 1){
20237            Roo.get(document).un("mousedown", onMouseDown);
20238            attached = false;
20239        }
20240    }
20241
20242    // private
20243    function onShow(m){
20244        var last = active.last();
20245        lastShow = new Date();
20246        active.add(m);
20247        if(!attached){
20248            Roo.get(document).on("mousedown", onMouseDown);
20249            attached = true;
20250        }
20251        if(m.parentMenu){
20252           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20253           m.parentMenu.activeChild = m;
20254        }else if(last && last.isVisible()){
20255           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20256        }
20257    }
20258
20259    // private
20260    function onBeforeHide(m){
20261        if(m.activeChild){
20262            m.activeChild.hide();
20263        }
20264        if(m.autoHideTimer){
20265            clearTimeout(m.autoHideTimer);
20266            delete m.autoHideTimer;
20267        }
20268    }
20269
20270    // private
20271    function onBeforeShow(m){
20272        var pm = m.parentMenu;
20273        if(!pm && !m.allowOtherMenus){
20274            hideAll();
20275        }else if(pm && pm.activeChild && active != m){
20276            pm.activeChild.hide();
20277        }
20278    }
20279
20280    // private
20281    function onMouseDown(e){
20282        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20283            hideAll();
20284        }
20285    }
20286
20287    // private
20288    function onBeforeCheck(mi, state){
20289        if(state){
20290            var g = groups[mi.group];
20291            for(var i = 0, l = g.length; i < l; i++){
20292                if(g[i] != mi){
20293                    g[i].setChecked(false);
20294                }
20295            }
20296        }
20297    }
20298
20299    return {
20300
20301        /**
20302         * Hides all menus that are currently visible
20303         */
20304        hideAll : function(){
20305             hideAll();  
20306        },
20307
20308        // private
20309        register : function(menu){
20310            if(!menus){
20311                init();
20312            }
20313            menus[menu.id] = menu;
20314            menu.on("beforehide", onBeforeHide);
20315            menu.on("hide", onHide);
20316            menu.on("beforeshow", onBeforeShow);
20317            menu.on("show", onShow);
20318            var g = menu.group;
20319            if(g && menu.events["checkchange"]){
20320                if(!groups[g]){
20321                    groups[g] = [];
20322                }
20323                groups[g].push(menu);
20324                menu.on("checkchange", onCheck);
20325            }
20326        },
20327
20328         /**
20329          * Returns a {@link Roo.menu.Menu} object
20330          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20331          * be used to generate and return a new Menu instance.
20332          */
20333        get : function(menu){
20334            if(typeof menu == "string"){ // menu id
20335                return menus[menu];
20336            }else if(menu.events){  // menu instance
20337                return menu;
20338            }else if(typeof menu.length == 'number'){ // array of menu items?
20339                return new Roo.menu.Menu({items:menu});
20340            }else{ // otherwise, must be a config
20341                return new Roo.menu.Menu(menu);
20342            }
20343        },
20344
20345        // private
20346        unregister : function(menu){
20347            delete menus[menu.id];
20348            menu.un("beforehide", onBeforeHide);
20349            menu.un("hide", onHide);
20350            menu.un("beforeshow", onBeforeShow);
20351            menu.un("show", onShow);
20352            var g = menu.group;
20353            if(g && menu.events["checkchange"]){
20354                groups[g].remove(menu);
20355                menu.un("checkchange", onCheck);
20356            }
20357        },
20358
20359        // private
20360        registerCheckable : function(menuItem){
20361            var g = menuItem.group;
20362            if(g){
20363                if(!groups[g]){
20364                    groups[g] = [];
20365                }
20366                groups[g].push(menuItem);
20367                menuItem.on("beforecheckchange", onBeforeCheck);
20368            }
20369        },
20370
20371        // private
20372        unregisterCheckable : function(menuItem){
20373            var g = menuItem.group;
20374            if(g){
20375                groups[g].remove(menuItem);
20376                menuItem.un("beforecheckchange", onBeforeCheck);
20377            }
20378        }
20379    };
20380 }();/*
20381  * Based on:
20382  * Ext JS Library 1.1.1
20383  * Copyright(c) 2006-2007, Ext JS, LLC.
20384  *
20385  * Originally Released Under LGPL - original licence link has changed is not relivant.
20386  *
20387  * Fork - LGPL
20388  * <script type="text/javascript">
20389  */
20390  
20391
20392 /**
20393  * @class Roo.menu.BaseItem
20394  * @extends Roo.Component
20395  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20396  * management and base configuration options shared by all menu components.
20397  * @constructor
20398  * Creates a new BaseItem
20399  * @param {Object} config Configuration options
20400  */
20401 Roo.menu.BaseItem = function(config){
20402     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20403
20404     this.addEvents({
20405         /**
20406          * @event click
20407          * Fires when this item is clicked
20408          * @param {Roo.menu.BaseItem} this
20409          * @param {Roo.EventObject} e
20410          */
20411         click: true,
20412         /**
20413          * @event activate
20414          * Fires when this item is activated
20415          * @param {Roo.menu.BaseItem} this
20416          */
20417         activate : true,
20418         /**
20419          * @event deactivate
20420          * Fires when this item is deactivated
20421          * @param {Roo.menu.BaseItem} this
20422          */
20423         deactivate : true
20424     });
20425
20426     if(this.handler){
20427         this.on("click", this.handler, this.scope, true);
20428     }
20429 };
20430
20431 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20432     /**
20433      * @cfg {Function} handler
20434      * A function that will handle the click event of this menu item (defaults to undefined)
20435      */
20436     /**
20437      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20438      */
20439     canActivate : false,
20440     /**
20441      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20442      */
20443     activeClass : "x-menu-item-active",
20444     /**
20445      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20446      */
20447     hideOnClick : true,
20448     /**
20449      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20450      */
20451     hideDelay : 100,
20452
20453     // private
20454     ctype: "Roo.menu.BaseItem",
20455
20456     // private
20457     actionMode : "container",
20458
20459     // private
20460     render : function(container, parentMenu){
20461         this.parentMenu = parentMenu;
20462         Roo.menu.BaseItem.superclass.render.call(this, container);
20463         this.container.menuItemId = this.id;
20464     },
20465
20466     // private
20467     onRender : function(container, position){
20468         this.el = Roo.get(this.el);
20469         container.dom.appendChild(this.el.dom);
20470     },
20471
20472     // private
20473     onClick : function(e){
20474         if(!this.disabled && this.fireEvent("click", this, e) !== false
20475                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20476             this.handleClick(e);
20477         }else{
20478             e.stopEvent();
20479         }
20480     },
20481
20482     // private
20483     activate : function(){
20484         if(this.disabled){
20485             return false;
20486         }
20487         var li = this.container;
20488         li.addClass(this.activeClass);
20489         this.region = li.getRegion().adjust(2, 2, -2, -2);
20490         this.fireEvent("activate", this);
20491         return true;
20492     },
20493
20494     // private
20495     deactivate : function(){
20496         this.container.removeClass(this.activeClass);
20497         this.fireEvent("deactivate", this);
20498     },
20499
20500     // private
20501     shouldDeactivate : function(e){
20502         return !this.region || !this.region.contains(e.getPoint());
20503     },
20504
20505     // private
20506     handleClick : function(e){
20507         if(this.hideOnClick){
20508             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20509         }
20510     },
20511
20512     // private
20513     expandMenu : function(autoActivate){
20514         // do nothing
20515     },
20516
20517     // private
20518     hideMenu : function(){
20519         // do nothing
20520     }
20521 });/*
20522  * Based on:
20523  * Ext JS Library 1.1.1
20524  * Copyright(c) 2006-2007, Ext JS, LLC.
20525  *
20526  * Originally Released Under LGPL - original licence link has changed is not relivant.
20527  *
20528  * Fork - LGPL
20529  * <script type="text/javascript">
20530  */
20531  
20532 /**
20533  * @class Roo.menu.Adapter
20534  * @extends Roo.menu.BaseItem
20535  * 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.
20536  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20537  * @constructor
20538  * Creates a new Adapter
20539  * @param {Object} config Configuration options
20540  */
20541 Roo.menu.Adapter = function(component, config){
20542     Roo.menu.Adapter.superclass.constructor.call(this, config);
20543     this.component = component;
20544 };
20545 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20546     // private
20547     canActivate : true,
20548
20549     // private
20550     onRender : function(container, position){
20551         this.component.render(container);
20552         this.el = this.component.getEl();
20553     },
20554
20555     // private
20556     activate : function(){
20557         if(this.disabled){
20558             return false;
20559         }
20560         this.component.focus();
20561         this.fireEvent("activate", this);
20562         return true;
20563     },
20564
20565     // private
20566     deactivate : function(){
20567         this.fireEvent("deactivate", this);
20568     },
20569
20570     // private
20571     disable : function(){
20572         this.component.disable();
20573         Roo.menu.Adapter.superclass.disable.call(this);
20574     },
20575
20576     // private
20577     enable : function(){
20578         this.component.enable();
20579         Roo.menu.Adapter.superclass.enable.call(this);
20580     }
20581 });/*
20582  * Based on:
20583  * Ext JS Library 1.1.1
20584  * Copyright(c) 2006-2007, Ext JS, LLC.
20585  *
20586  * Originally Released Under LGPL - original licence link has changed is not relivant.
20587  *
20588  * Fork - LGPL
20589  * <script type="text/javascript">
20590  */
20591
20592 /**
20593  * @class Roo.menu.TextItem
20594  * @extends Roo.menu.BaseItem
20595  * Adds a static text string to a menu, usually used as either a heading or group separator.
20596  * Note: old style constructor with text is still supported.
20597  * 
20598  * @constructor
20599  * Creates a new TextItem
20600  * @param {Object} cfg Configuration
20601  */
20602 Roo.menu.TextItem = function(cfg){
20603     if (typeof(cfg) == 'string') {
20604         this.text = cfg;
20605     } else {
20606         Roo.apply(this,cfg);
20607     }
20608     
20609     Roo.menu.TextItem.superclass.constructor.call(this);
20610 };
20611
20612 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20613     /**
20614      * @cfg {Boolean} text Text to show on item.
20615      */
20616     text : '',
20617     
20618     /**
20619      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20620      */
20621     hideOnClick : false,
20622     /**
20623      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20624      */
20625     itemCls : "x-menu-text",
20626
20627     // private
20628     onRender : function(){
20629         var s = document.createElement("span");
20630         s.className = this.itemCls;
20631         s.innerHTML = this.text;
20632         this.el = s;
20633         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20634     }
20635 });/*
20636  * Based on:
20637  * Ext JS Library 1.1.1
20638  * Copyright(c) 2006-2007, Ext JS, LLC.
20639  *
20640  * Originally Released Under LGPL - original licence link has changed is not relivant.
20641  *
20642  * Fork - LGPL
20643  * <script type="text/javascript">
20644  */
20645
20646 /**
20647  * @class Roo.menu.Separator
20648  * @extends Roo.menu.BaseItem
20649  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20650  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20651  * @constructor
20652  * @param {Object} config Configuration options
20653  */
20654 Roo.menu.Separator = function(config){
20655     Roo.menu.Separator.superclass.constructor.call(this, config);
20656 };
20657
20658 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20659     /**
20660      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20661      */
20662     itemCls : "x-menu-sep",
20663     /**
20664      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20665      */
20666     hideOnClick : false,
20667
20668     // private
20669     onRender : function(li){
20670         var s = document.createElement("span");
20671         s.className = this.itemCls;
20672         s.innerHTML = "&#160;";
20673         this.el = s;
20674         li.addClass("x-menu-sep-li");
20675         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20676     }
20677 });/*
20678  * Based on:
20679  * Ext JS Library 1.1.1
20680  * Copyright(c) 2006-2007, Ext JS, LLC.
20681  *
20682  * Originally Released Under LGPL - original licence link has changed is not relivant.
20683  *
20684  * Fork - LGPL
20685  * <script type="text/javascript">
20686  */
20687 /**
20688  * @class Roo.menu.Item
20689  * @extends Roo.menu.BaseItem
20690  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20691  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20692  * activation and click handling.
20693  * @constructor
20694  * Creates a new Item
20695  * @param {Object} config Configuration options
20696  */
20697 Roo.menu.Item = function(config){
20698     Roo.menu.Item.superclass.constructor.call(this, config);
20699     if(this.menu){
20700         this.menu = Roo.menu.MenuMgr.get(this.menu);
20701     }
20702 };
20703 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20704     
20705     /**
20706      * @cfg {String} text
20707      * The text to show on the menu item.
20708      */
20709     text: '',
20710      /**
20711      * @cfg {String} HTML to render in menu
20712      * The text to show on the menu item (HTML version).
20713      */
20714     html: '',
20715     /**
20716      * @cfg {String} icon
20717      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20718      */
20719     icon: undefined,
20720     /**
20721      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20722      */
20723     itemCls : "x-menu-item",
20724     /**
20725      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20726      */
20727     canActivate : true,
20728     /**
20729      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20730      */
20731     showDelay: 200,
20732     // doc'd in BaseItem
20733     hideDelay: 200,
20734
20735     // private
20736     ctype: "Roo.menu.Item",
20737     
20738     // private
20739     onRender : function(container, position){
20740         var el = document.createElement("a");
20741         el.hideFocus = true;
20742         el.unselectable = "on";
20743         el.href = this.href || "#";
20744         if(this.hrefTarget){
20745             el.target = this.hrefTarget;
20746         }
20747         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20748         
20749         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20750         
20751         el.innerHTML = String.format(
20752                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20753                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20754         this.el = el;
20755         Roo.menu.Item.superclass.onRender.call(this, container, position);
20756     },
20757
20758     /**
20759      * Sets the text to display in this menu item
20760      * @param {String} text The text to display
20761      * @param {Boolean} isHTML true to indicate text is pure html.
20762      */
20763     setText : function(text, isHTML){
20764         if (isHTML) {
20765             this.html = text;
20766         } else {
20767             this.text = text;
20768             this.html = '';
20769         }
20770         if(this.rendered){
20771             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20772      
20773             this.el.update(String.format(
20774                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20775                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20776             this.parentMenu.autoWidth();
20777         }
20778     },
20779
20780     // private
20781     handleClick : function(e){
20782         if(!this.href){ // if no link defined, stop the event automatically
20783             e.stopEvent();
20784         }
20785         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20786     },
20787
20788     // private
20789     activate : function(autoExpand){
20790         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20791             this.focus();
20792             if(autoExpand){
20793                 this.expandMenu();
20794             }
20795         }
20796         return true;
20797     },
20798
20799     // private
20800     shouldDeactivate : function(e){
20801         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20802             if(this.menu && this.menu.isVisible()){
20803                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20804             }
20805             return true;
20806         }
20807         return false;
20808     },
20809
20810     // private
20811     deactivate : function(){
20812         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20813         this.hideMenu();
20814     },
20815
20816     // private
20817     expandMenu : function(autoActivate){
20818         if(!this.disabled && this.menu){
20819             clearTimeout(this.hideTimer);
20820             delete this.hideTimer;
20821             if(!this.menu.isVisible() && !this.showTimer){
20822                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20823             }else if (this.menu.isVisible() && autoActivate){
20824                 this.menu.tryActivate(0, 1);
20825             }
20826         }
20827     },
20828
20829     // private
20830     deferExpand : function(autoActivate){
20831         delete this.showTimer;
20832         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20833         if(autoActivate){
20834             this.menu.tryActivate(0, 1);
20835         }
20836     },
20837
20838     // private
20839     hideMenu : function(){
20840         clearTimeout(this.showTimer);
20841         delete this.showTimer;
20842         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20843             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20844         }
20845     },
20846
20847     // private
20848     deferHide : function(){
20849         delete this.hideTimer;
20850         this.menu.hide();
20851     }
20852 });/*
20853  * Based on:
20854  * Ext JS Library 1.1.1
20855  * Copyright(c) 2006-2007, Ext JS, LLC.
20856  *
20857  * Originally Released Under LGPL - original licence link has changed is not relivant.
20858  *
20859  * Fork - LGPL
20860  * <script type="text/javascript">
20861  */
20862  
20863 /**
20864  * @class Roo.menu.CheckItem
20865  * @extends Roo.menu.Item
20866  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20867  * @constructor
20868  * Creates a new CheckItem
20869  * @param {Object} config Configuration options
20870  */
20871 Roo.menu.CheckItem = function(config){
20872     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20873     this.addEvents({
20874         /**
20875          * @event beforecheckchange
20876          * Fires before the checked value is set, providing an opportunity to cancel if needed
20877          * @param {Roo.menu.CheckItem} this
20878          * @param {Boolean} checked The new checked value that will be set
20879          */
20880         "beforecheckchange" : true,
20881         /**
20882          * @event checkchange
20883          * Fires after the checked value has been set
20884          * @param {Roo.menu.CheckItem} this
20885          * @param {Boolean} checked The checked value that was set
20886          */
20887         "checkchange" : true
20888     });
20889     if(this.checkHandler){
20890         this.on('checkchange', this.checkHandler, this.scope);
20891     }
20892 };
20893 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20894     /**
20895      * @cfg {String} group
20896      * All check items with the same group name will automatically be grouped into a single-select
20897      * radio button group (defaults to '')
20898      */
20899     /**
20900      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20901      */
20902     itemCls : "x-menu-item x-menu-check-item",
20903     /**
20904      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20905      */
20906     groupClass : "x-menu-group-item",
20907
20908     /**
20909      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20910      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20911      * initialized with checked = true will be rendered as checked.
20912      */
20913     checked: false,
20914
20915     // private
20916     ctype: "Roo.menu.CheckItem",
20917
20918     // private
20919     onRender : function(c){
20920         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20921         if(this.group){
20922             this.el.addClass(this.groupClass);
20923         }
20924         Roo.menu.MenuMgr.registerCheckable(this);
20925         if(this.checked){
20926             this.checked = false;
20927             this.setChecked(true, true);
20928         }
20929     },
20930
20931     // private
20932     destroy : function(){
20933         if(this.rendered){
20934             Roo.menu.MenuMgr.unregisterCheckable(this);
20935         }
20936         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20937     },
20938
20939     /**
20940      * Set the checked state of this item
20941      * @param {Boolean} checked The new checked value
20942      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20943      */
20944     setChecked : function(state, suppressEvent){
20945         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20946             if(this.container){
20947                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20948             }
20949             this.checked = state;
20950             if(suppressEvent !== true){
20951                 this.fireEvent("checkchange", this, state);
20952             }
20953         }
20954     },
20955
20956     // private
20957     handleClick : function(e){
20958        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20959            this.setChecked(!this.checked);
20960        }
20961        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20962     }
20963 });/*
20964  * Based on:
20965  * Ext JS Library 1.1.1
20966  * Copyright(c) 2006-2007, Ext JS, LLC.
20967  *
20968  * Originally Released Under LGPL - original licence link has changed is not relivant.
20969  *
20970  * Fork - LGPL
20971  * <script type="text/javascript">
20972  */
20973  
20974 /**
20975  * @class Roo.menu.DateItem
20976  * @extends Roo.menu.Adapter
20977  * A menu item that wraps the {@link Roo.DatPicker} component.
20978  * @constructor
20979  * Creates a new DateItem
20980  * @param {Object} config Configuration options
20981  */
20982 Roo.menu.DateItem = function(config){
20983     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20984     /** The Roo.DatePicker object @type Roo.DatePicker */
20985     this.picker = this.component;
20986     this.addEvents({select: true});
20987     
20988     this.picker.on("render", function(picker){
20989         picker.getEl().swallowEvent("click");
20990         picker.container.addClass("x-menu-date-item");
20991     });
20992
20993     this.picker.on("select", this.onSelect, this);
20994 };
20995
20996 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20997     // private
20998     onSelect : function(picker, date){
20999         this.fireEvent("select", this, date, picker);
21000         Roo.menu.DateItem.superclass.handleClick.call(this);
21001     }
21002 });/*
21003  * Based on:
21004  * Ext JS Library 1.1.1
21005  * Copyright(c) 2006-2007, Ext JS, LLC.
21006  *
21007  * Originally Released Under LGPL - original licence link has changed is not relivant.
21008  *
21009  * Fork - LGPL
21010  * <script type="text/javascript">
21011  */
21012  
21013 /**
21014  * @class Roo.menu.ColorItem
21015  * @extends Roo.menu.Adapter
21016  * A menu item that wraps the {@link Roo.ColorPalette} component.
21017  * @constructor
21018  * Creates a new ColorItem
21019  * @param {Object} config Configuration options
21020  */
21021 Roo.menu.ColorItem = function(config){
21022     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21023     /** The Roo.ColorPalette object @type Roo.ColorPalette */
21024     this.palette = this.component;
21025     this.relayEvents(this.palette, ["select"]);
21026     if(this.selectHandler){
21027         this.on('select', this.selectHandler, this.scope);
21028     }
21029 };
21030 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21031  * Based on:
21032  * Ext JS Library 1.1.1
21033  * Copyright(c) 2006-2007, Ext JS, LLC.
21034  *
21035  * Originally Released Under LGPL - original licence link has changed is not relivant.
21036  *
21037  * Fork - LGPL
21038  * <script type="text/javascript">
21039  */
21040  
21041
21042 /**
21043  * @class Roo.menu.DateMenu
21044  * @extends Roo.menu.Menu
21045  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21046  * @constructor
21047  * Creates a new DateMenu
21048  * @param {Object} config Configuration options
21049  */
21050 Roo.menu.DateMenu = function(config){
21051     Roo.menu.DateMenu.superclass.constructor.call(this, config);
21052     this.plain = true;
21053     var di = new Roo.menu.DateItem(config);
21054     this.add(di);
21055     /**
21056      * The {@link Roo.DatePicker} instance for this DateMenu
21057      * @type DatePicker
21058      */
21059     this.picker = di.picker;
21060     /**
21061      * @event select
21062      * @param {DatePicker} picker
21063      * @param {Date} date
21064      */
21065     this.relayEvents(di, ["select"]);
21066     this.on('beforeshow', function(){
21067         if(this.picker){
21068             this.picker.hideMonthPicker(false);
21069         }
21070     }, this);
21071 };
21072 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21073     cls:'x-date-menu'
21074 });/*
21075  * Based on:
21076  * Ext JS Library 1.1.1
21077  * Copyright(c) 2006-2007, Ext JS, LLC.
21078  *
21079  * Originally Released Under LGPL - original licence link has changed is not relivant.
21080  *
21081  * Fork - LGPL
21082  * <script type="text/javascript">
21083  */
21084  
21085
21086 /**
21087  * @class Roo.menu.ColorMenu
21088  * @extends Roo.menu.Menu
21089  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21090  * @constructor
21091  * Creates a new ColorMenu
21092  * @param {Object} config Configuration options
21093  */
21094 Roo.menu.ColorMenu = function(config){
21095     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21096     this.plain = true;
21097     var ci = new Roo.menu.ColorItem(config);
21098     this.add(ci);
21099     /**
21100      * The {@link Roo.ColorPalette} instance for this ColorMenu
21101      * @type ColorPalette
21102      */
21103     this.palette = ci.palette;
21104     /**
21105      * @event select
21106      * @param {ColorPalette} palette
21107      * @param {String} color
21108      */
21109     this.relayEvents(ci, ["select"]);
21110 };
21111 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21112  * Based on:
21113  * Ext JS Library 1.1.1
21114  * Copyright(c) 2006-2007, Ext JS, LLC.
21115  *
21116  * Originally Released Under LGPL - original licence link has changed is not relivant.
21117  *
21118  * Fork - LGPL
21119  * <script type="text/javascript">
21120  */
21121  
21122 /**
21123  * @class Roo.form.Field
21124  * @extends Roo.BoxComponent
21125  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21126  * @constructor
21127  * Creates a new Field
21128  * @param {Object} config Configuration options
21129  */
21130 Roo.form.Field = function(config){
21131     Roo.form.Field.superclass.constructor.call(this, config);
21132 };
21133
21134 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21135     /**
21136      * @cfg {String} fieldLabel Label to use when rendering a form.
21137      */
21138        /**
21139      * @cfg {String} qtip Mouse over tip
21140      */
21141      
21142     /**
21143      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21144      */
21145     invalidClass : "x-form-invalid",
21146     /**
21147      * @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")
21148      */
21149     invalidText : "The value in this field is invalid",
21150     /**
21151      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21152      */
21153     focusClass : "x-form-focus",
21154     /**
21155      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21156       automatic validation (defaults to "keyup").
21157      */
21158     validationEvent : "keyup",
21159     /**
21160      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21161      */
21162     validateOnBlur : true,
21163     /**
21164      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21165      */
21166     validationDelay : 250,
21167     /**
21168      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21169      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21170      */
21171     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21172     /**
21173      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21174      */
21175     fieldClass : "x-form-field",
21176     /**
21177      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21178      *<pre>
21179 Value         Description
21180 -----------   ----------------------------------------------------------------------
21181 qtip          Display a quick tip when the user hovers over the field
21182 title         Display a default browser title attribute popup
21183 under         Add a block div beneath the field containing the error text
21184 side          Add an error icon to the right of the field with a popup on hover
21185 [element id]  Add the error text directly to the innerHTML of the specified element
21186 </pre>
21187      */
21188     msgTarget : 'qtip',
21189     /**
21190      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21191      */
21192     msgFx : 'normal',
21193
21194     /**
21195      * @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.
21196      */
21197     readOnly : false,
21198
21199     /**
21200      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21201      */
21202     disabled : false,
21203
21204     /**
21205      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21206      */
21207     inputType : undefined,
21208     
21209     /**
21210      * @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).
21211          */
21212         tabIndex : undefined,
21213         
21214     // private
21215     isFormField : true,
21216
21217     // private
21218     hasFocus : false,
21219     /**
21220      * @property {Roo.Element} fieldEl
21221      * Element Containing the rendered Field (with label etc.)
21222      */
21223     /**
21224      * @cfg {Mixed} value A value to initialize this field with.
21225      */
21226     value : undefined,
21227
21228     /**
21229      * @cfg {String} name The field's HTML name attribute.
21230      */
21231     /**
21232      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21233      */
21234
21235         // private ??
21236         initComponent : function(){
21237         Roo.form.Field.superclass.initComponent.call(this);
21238         this.addEvents({
21239             /**
21240              * @event focus
21241              * Fires when this field receives input focus.
21242              * @param {Roo.form.Field} this
21243              */
21244             focus : true,
21245             /**
21246              * @event blur
21247              * Fires when this field loses input focus.
21248              * @param {Roo.form.Field} this
21249              */
21250             blur : true,
21251             /**
21252              * @event specialkey
21253              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21254              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21255              * @param {Roo.form.Field} this
21256              * @param {Roo.EventObject} e The event object
21257              */
21258             specialkey : true,
21259             /**
21260              * @event change
21261              * Fires just before the field blurs if the field value has changed.
21262              * @param {Roo.form.Field} this
21263              * @param {Mixed} newValue The new value
21264              * @param {Mixed} oldValue The original value
21265              */
21266             change : true,
21267             /**
21268              * @event invalid
21269              * Fires after the field has been marked as invalid.
21270              * @param {Roo.form.Field} this
21271              * @param {String} msg The validation message
21272              */
21273             invalid : true,
21274             /**
21275              * @event valid
21276              * Fires after the field has been validated with no errors.
21277              * @param {Roo.form.Field} this
21278              */
21279             valid : true,
21280              /**
21281              * @event keyup
21282              * Fires after the key up
21283              * @param {Roo.form.Field} this
21284              * @param {Roo.EventObject}  e The event Object
21285              */
21286             keyup : true
21287         });
21288     },
21289
21290     /**
21291      * Returns the name attribute of the field if available
21292      * @return {String} name The field name
21293      */
21294     getName: function(){
21295          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21296     },
21297
21298     // private
21299     onRender : function(ct, position){
21300         Roo.form.Field.superclass.onRender.call(this, ct, position);
21301         if(!this.el){
21302             var cfg = this.getAutoCreate();
21303             if(!cfg.name){
21304                 cfg.name = this.name || this.id;
21305             }
21306             if(this.inputType){
21307                 cfg.type = this.inputType;
21308             }
21309             this.el = ct.createChild(cfg, position);
21310         }
21311         var type = this.el.dom.type;
21312         if(type){
21313             if(type == 'password'){
21314                 type = 'text';
21315             }
21316             this.el.addClass('x-form-'+type);
21317         }
21318         if(this.readOnly){
21319             this.el.dom.readOnly = true;
21320         }
21321         if(this.tabIndex !== undefined){
21322             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21323         }
21324
21325         this.el.addClass([this.fieldClass, this.cls]);
21326         this.initValue();
21327     },
21328
21329     /**
21330      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21331      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21332      * @return {Roo.form.Field} this
21333      */
21334     applyTo : function(target){
21335         this.allowDomMove = false;
21336         this.el = Roo.get(target);
21337         this.render(this.el.dom.parentNode);
21338         return this;
21339     },
21340
21341     // private
21342     initValue : function(){
21343         if(this.value !== undefined){
21344             this.setValue(this.value);
21345         }else if(this.el.dom.value.length > 0){
21346             this.setValue(this.el.dom.value);
21347         }
21348     },
21349
21350     /**
21351      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21352      */
21353     isDirty : function() {
21354         if(this.disabled) {
21355             return false;
21356         }
21357         return String(this.getValue()) !== String(this.originalValue);
21358     },
21359
21360     // private
21361     afterRender : function(){
21362         Roo.form.Field.superclass.afterRender.call(this);
21363         this.initEvents();
21364     },
21365
21366     // private
21367     fireKey : function(e){
21368         //Roo.log('field ' + e.getKey());
21369         if(e.isNavKeyPress()){
21370             this.fireEvent("specialkey", this, e);
21371         }
21372     },
21373
21374     /**
21375      * Resets the current field value to the originally loaded value and clears any validation messages
21376      */
21377     reset : function(){
21378         this.setValue(this.originalValue);
21379         this.clearInvalid();
21380     },
21381
21382     // private
21383     initEvents : function(){
21384         // safari killled keypress - so keydown is now used..
21385         this.el.on("keydown" , this.fireKey,  this);
21386         this.el.on("focus", this.onFocus,  this);
21387         this.el.on("blur", this.onBlur,  this);
21388         this.el.relayEvent('keyup', this);
21389
21390         // reference to original value for reset
21391         this.originalValue = this.getValue();
21392     },
21393
21394     // private
21395     onFocus : function(){
21396         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21397             this.el.addClass(this.focusClass);
21398         }
21399         if(!this.hasFocus){
21400             this.hasFocus = true;
21401             this.startValue = this.getValue();
21402             this.fireEvent("focus", this);
21403         }
21404     },
21405
21406     beforeBlur : Roo.emptyFn,
21407
21408     // private
21409     onBlur : function(){
21410         this.beforeBlur();
21411         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21412             this.el.removeClass(this.focusClass);
21413         }
21414         this.hasFocus = false;
21415         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21416             this.validate();
21417         }
21418         var v = this.getValue();
21419         if(String(v) !== String(this.startValue)){
21420             this.fireEvent('change', this, v, this.startValue);
21421         }
21422         this.fireEvent("blur", this);
21423     },
21424
21425     /**
21426      * Returns whether or not the field value is currently valid
21427      * @param {Boolean} preventMark True to disable marking the field invalid
21428      * @return {Boolean} True if the value is valid, else false
21429      */
21430     isValid : function(preventMark){
21431         if(this.disabled){
21432             return true;
21433         }
21434         var restore = this.preventMark;
21435         this.preventMark = preventMark === true;
21436         var v = this.validateValue(this.processValue(this.getRawValue()));
21437         this.preventMark = restore;
21438         return v;
21439     },
21440
21441     /**
21442      * Validates the field value
21443      * @return {Boolean} True if the value is valid, else false
21444      */
21445     validate : function(){
21446         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21447             this.clearInvalid();
21448             return true;
21449         }
21450         return false;
21451     },
21452
21453     processValue : function(value){
21454         return value;
21455     },
21456
21457     // private
21458     // Subclasses should provide the validation implementation by overriding this
21459     validateValue : function(value){
21460         return true;
21461     },
21462
21463     /**
21464      * Mark this field as invalid
21465      * @param {String} msg The validation message
21466      */
21467     markInvalid : function(msg){
21468         if(!this.rendered || this.preventMark){ // not rendered
21469             return;
21470         }
21471         this.el.addClass(this.invalidClass);
21472         msg = msg || this.invalidText;
21473         switch(this.msgTarget){
21474             case 'qtip':
21475                 this.el.dom.qtip = msg;
21476                 this.el.dom.qclass = 'x-form-invalid-tip';
21477                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21478                     Roo.QuickTips.enable();
21479                 }
21480                 break;
21481             case 'title':
21482                 this.el.dom.title = msg;
21483                 break;
21484             case 'under':
21485                 if(!this.errorEl){
21486                     var elp = this.el.findParent('.x-form-element', 5, true);
21487                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21488                     this.errorEl.setWidth(elp.getWidth(true)-20);
21489                 }
21490                 this.errorEl.update(msg);
21491                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21492                 break;
21493             case 'side':
21494                 if(!this.errorIcon){
21495                     var elp = this.el.findParent('.x-form-element', 5, true);
21496                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21497                 }
21498                 this.alignErrorIcon();
21499                 this.errorIcon.dom.qtip = msg;
21500                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21501                 this.errorIcon.show();
21502                 this.on('resize', this.alignErrorIcon, this);
21503                 break;
21504             default:
21505                 var t = Roo.getDom(this.msgTarget);
21506                 t.innerHTML = msg;
21507                 t.style.display = this.msgDisplay;
21508                 break;
21509         }
21510         this.fireEvent('invalid', this, msg);
21511     },
21512
21513     // private
21514     alignErrorIcon : function(){
21515         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21516     },
21517
21518     /**
21519      * Clear any invalid styles/messages for this field
21520      */
21521     clearInvalid : function(){
21522         if(!this.rendered || this.preventMark){ // not rendered
21523             return;
21524         }
21525         this.el.removeClass(this.invalidClass);
21526         switch(this.msgTarget){
21527             case 'qtip':
21528                 this.el.dom.qtip = '';
21529                 break;
21530             case 'title':
21531                 this.el.dom.title = '';
21532                 break;
21533             case 'under':
21534                 if(this.errorEl){
21535                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21536                 }
21537                 break;
21538             case 'side':
21539                 if(this.errorIcon){
21540                     this.errorIcon.dom.qtip = '';
21541                     this.errorIcon.hide();
21542                     this.un('resize', this.alignErrorIcon, this);
21543                 }
21544                 break;
21545             default:
21546                 var t = Roo.getDom(this.msgTarget);
21547                 t.innerHTML = '';
21548                 t.style.display = 'none';
21549                 break;
21550         }
21551         this.fireEvent('valid', this);
21552     },
21553
21554     /**
21555      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21556      * @return {Mixed} value The field value
21557      */
21558     getRawValue : function(){
21559         var v = this.el.getValue();
21560         if(v === this.emptyText){
21561             v = '';
21562         }
21563         return v;
21564     },
21565
21566     /**
21567      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21568      * @return {Mixed} value The field value
21569      */
21570     getValue : function(){
21571         var v = this.el.getValue();
21572         if(v === this.emptyText || v === undefined){
21573             v = '';
21574         }
21575         return v;
21576     },
21577
21578     /**
21579      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21580      * @param {Mixed} value The value to set
21581      */
21582     setRawValue : function(v){
21583         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21584     },
21585
21586     /**
21587      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21588      * @param {Mixed} value The value to set
21589      */
21590     setValue : function(v){
21591         this.value = v;
21592         if(this.rendered){
21593             this.el.dom.value = (v === null || v === undefined ? '' : v);
21594              this.validate();
21595         }
21596     },
21597
21598     adjustSize : function(w, h){
21599         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21600         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21601         return s;
21602     },
21603
21604     adjustWidth : function(tag, w){
21605         tag = tag.toLowerCase();
21606         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21607             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21608                 if(tag == 'input'){
21609                     return w + 2;
21610                 }
21611                 if(tag = 'textarea'){
21612                     return w-2;
21613                 }
21614             }else if(Roo.isOpera){
21615                 if(tag == 'input'){
21616                     return w + 2;
21617                 }
21618                 if(tag = 'textarea'){
21619                     return w-2;
21620                 }
21621             }
21622         }
21623         return w;
21624     }
21625 });
21626
21627
21628 // anything other than normal should be considered experimental
21629 Roo.form.Field.msgFx = {
21630     normal : {
21631         show: function(msgEl, f){
21632             msgEl.setDisplayed('block');
21633         },
21634
21635         hide : function(msgEl, f){
21636             msgEl.setDisplayed(false).update('');
21637         }
21638     },
21639
21640     slide : {
21641         show: function(msgEl, f){
21642             msgEl.slideIn('t', {stopFx:true});
21643         },
21644
21645         hide : function(msgEl, f){
21646             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21647         }
21648     },
21649
21650     slideRight : {
21651         show: function(msgEl, f){
21652             msgEl.fixDisplay();
21653             msgEl.alignTo(f.el, 'tl-tr');
21654             msgEl.slideIn('l', {stopFx:true});
21655         },
21656
21657         hide : function(msgEl, f){
21658             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21659         }
21660     }
21661 };/*
21662  * Based on:
21663  * Ext JS Library 1.1.1
21664  * Copyright(c) 2006-2007, Ext JS, LLC.
21665  *
21666  * Originally Released Under LGPL - original licence link has changed is not relivant.
21667  *
21668  * Fork - LGPL
21669  * <script type="text/javascript">
21670  */
21671  
21672
21673 /**
21674  * @class Roo.form.TextField
21675  * @extends Roo.form.Field
21676  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21677  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21678  * @constructor
21679  * Creates a new TextField
21680  * @param {Object} config Configuration options
21681  */
21682 Roo.form.TextField = function(config){
21683     Roo.form.TextField.superclass.constructor.call(this, config);
21684     this.addEvents({
21685         /**
21686          * @event autosize
21687          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21688          * according to the default logic, but this event provides a hook for the developer to apply additional
21689          * logic at runtime to resize the field if needed.
21690              * @param {Roo.form.Field} this This text field
21691              * @param {Number} width The new field width
21692              */
21693         autosize : true
21694     });
21695 };
21696
21697 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21698     /**
21699      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21700      */
21701     grow : false,
21702     /**
21703      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21704      */
21705     growMin : 30,
21706     /**
21707      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21708      */
21709     growMax : 800,
21710     /**
21711      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21712      */
21713     vtype : null,
21714     /**
21715      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21716      */
21717     maskRe : null,
21718     /**
21719      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21720      */
21721     disableKeyFilter : false,
21722     /**
21723      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21724      */
21725     allowBlank : true,
21726     /**
21727      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21728      */
21729     minLength : 0,
21730     /**
21731      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21732      */
21733     maxLength : Number.MAX_VALUE,
21734     /**
21735      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21736      */
21737     minLengthText : "The minimum length for this field is {0}",
21738     /**
21739      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21740      */
21741     maxLengthText : "The maximum length for this field is {0}",
21742     /**
21743      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21744      */
21745     selectOnFocus : false,
21746     /**
21747      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21748      */
21749     blankText : "This field is required",
21750     /**
21751      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21752      * If available, this function will be called only after the basic validators all return true, and will be passed the
21753      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21754      */
21755     validator : null,
21756     /**
21757      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21758      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21759      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21760      */
21761     regex : null,
21762     /**
21763      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21764      */
21765     regexText : "",
21766     /**
21767      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21768      */
21769     emptyText : null,
21770     /**
21771      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21772      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21773      */
21774     emptyClass : 'x-form-empty-field',
21775
21776     // private
21777     initEvents : function(){
21778         Roo.form.TextField.superclass.initEvents.call(this);
21779         if(this.validationEvent == 'keyup'){
21780             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21781             this.el.on('keyup', this.filterValidation, this);
21782         }
21783         else if(this.validationEvent !== false){
21784             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21785         }
21786         if(this.selectOnFocus || this.emptyText){
21787             this.on("focus", this.preFocus, this);
21788             if(this.emptyText){
21789                 this.on('blur', this.postBlur, this);
21790                 this.applyEmptyText();
21791             }
21792         }
21793         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21794             this.el.on("keypress", this.filterKeys, this);
21795         }
21796         if(this.grow){
21797             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21798             this.el.on("click", this.autoSize,  this);
21799         }
21800     },
21801
21802     processValue : function(value){
21803         if(this.stripCharsRe){
21804             var newValue = value.replace(this.stripCharsRe, '');
21805             if(newValue !== value){
21806                 this.setRawValue(newValue);
21807                 return newValue;
21808             }
21809         }
21810         return value;
21811     },
21812
21813     filterValidation : function(e){
21814         if(!e.isNavKeyPress()){
21815             this.validationTask.delay(this.validationDelay);
21816         }
21817     },
21818
21819     // private
21820     onKeyUp : function(e){
21821         if(!e.isNavKeyPress()){
21822             this.autoSize();
21823         }
21824     },
21825
21826     /**
21827      * Resets the current field value to the originally-loaded value and clears any validation messages.
21828      * Also adds emptyText and emptyClass if the original value was blank.
21829      */
21830     reset : function(){
21831         Roo.form.TextField.superclass.reset.call(this);
21832         this.applyEmptyText();
21833     },
21834
21835     applyEmptyText : function(){
21836         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21837             this.setRawValue(this.emptyText);
21838             this.el.addClass(this.emptyClass);
21839         }
21840     },
21841
21842     // private
21843     preFocus : function(){
21844         if(this.emptyText){
21845             if(this.el.dom.value == this.emptyText){
21846                 this.setRawValue('');
21847             }
21848             this.el.removeClass(this.emptyClass);
21849         }
21850         if(this.selectOnFocus){
21851             this.el.dom.select();
21852         }
21853     },
21854
21855     // private
21856     postBlur : function(){
21857         this.applyEmptyText();
21858     },
21859
21860     // private
21861     filterKeys : function(e){
21862         var k = e.getKey();
21863         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21864             return;
21865         }
21866         var c = e.getCharCode(), cc = String.fromCharCode(c);
21867         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21868             return;
21869         }
21870         if(!this.maskRe.test(cc)){
21871             e.stopEvent();
21872         }
21873     },
21874
21875     setValue : function(v){
21876         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21877             this.el.removeClass(this.emptyClass);
21878         }
21879         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21880         this.applyEmptyText();
21881         this.autoSize();
21882     },
21883
21884     /**
21885      * Validates a value according to the field's validation rules and marks the field as invalid
21886      * if the validation fails
21887      * @param {Mixed} value The value to validate
21888      * @return {Boolean} True if the value is valid, else false
21889      */
21890     validateValue : function(value){
21891         if(value.length < 1 || value === this.emptyText){ // if it's blank
21892              if(this.allowBlank){
21893                 this.clearInvalid();
21894                 return true;
21895              }else{
21896                 this.markInvalid(this.blankText);
21897                 return false;
21898              }
21899         }
21900         if(value.length < this.minLength){
21901             this.markInvalid(String.format(this.minLengthText, this.minLength));
21902             return false;
21903         }
21904         if(value.length > this.maxLength){
21905             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21906             return false;
21907         }
21908         if(this.vtype){
21909             var vt = Roo.form.VTypes;
21910             if(!vt[this.vtype](value, this)){
21911                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21912                 return false;
21913             }
21914         }
21915         if(typeof this.validator == "function"){
21916             var msg = this.validator(value);
21917             if(msg !== true){
21918                 this.markInvalid(msg);
21919                 return false;
21920             }
21921         }
21922         if(this.regex && !this.regex.test(value)){
21923             this.markInvalid(this.regexText);
21924             return false;
21925         }
21926         return true;
21927     },
21928
21929     /**
21930      * Selects text in this field
21931      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21932      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21933      */
21934     selectText : function(start, end){
21935         var v = this.getRawValue();
21936         if(v.length > 0){
21937             start = start === undefined ? 0 : start;
21938             end = end === undefined ? v.length : end;
21939             var d = this.el.dom;
21940             if(d.setSelectionRange){
21941                 d.setSelectionRange(start, end);
21942             }else if(d.createTextRange){
21943                 var range = d.createTextRange();
21944                 range.moveStart("character", start);
21945                 range.moveEnd("character", v.length-end);
21946                 range.select();
21947             }
21948         }
21949     },
21950
21951     /**
21952      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21953      * This only takes effect if grow = true, and fires the autosize event.
21954      */
21955     autoSize : function(){
21956         if(!this.grow || !this.rendered){
21957             return;
21958         }
21959         if(!this.metrics){
21960             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21961         }
21962         var el = this.el;
21963         var v = el.dom.value;
21964         var d = document.createElement('div');
21965         d.appendChild(document.createTextNode(v));
21966         v = d.innerHTML;
21967         d = null;
21968         v += "&#160;";
21969         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21970         this.el.setWidth(w);
21971         this.fireEvent("autosize", this, w);
21972     }
21973 });/*
21974  * Based on:
21975  * Ext JS Library 1.1.1
21976  * Copyright(c) 2006-2007, Ext JS, LLC.
21977  *
21978  * Originally Released Under LGPL - original licence link has changed is not relivant.
21979  *
21980  * Fork - LGPL
21981  * <script type="text/javascript">
21982  */
21983  
21984 /**
21985  * @class Roo.form.Hidden
21986  * @extends Roo.form.TextField
21987  * Simple Hidden element used on forms 
21988  * 
21989  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21990  * 
21991  * @constructor
21992  * Creates a new Hidden form element.
21993  * @param {Object} config Configuration options
21994  */
21995
21996
21997
21998 // easy hidden field...
21999 Roo.form.Hidden = function(config){
22000     Roo.form.Hidden.superclass.constructor.call(this, config);
22001 };
22002   
22003 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
22004     fieldLabel:      '',
22005     inputType:      'hidden',
22006     width:          50,
22007     allowBlank:     true,
22008     labelSeparator: '',
22009     hidden:         true,
22010     itemCls :       'x-form-item-display-none'
22011
22012
22013 });
22014
22015
22016 /*
22017  * Based on:
22018  * Ext JS Library 1.1.1
22019  * Copyright(c) 2006-2007, Ext JS, LLC.
22020  *
22021  * Originally Released Under LGPL - original licence link has changed is not relivant.
22022  *
22023  * Fork - LGPL
22024  * <script type="text/javascript">
22025  */
22026  
22027 /**
22028  * @class Roo.form.TriggerField
22029  * @extends Roo.form.TextField
22030  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22031  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22032  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22033  * for which you can provide a custom implementation.  For example:
22034  * <pre><code>
22035 var trigger = new Roo.form.TriggerField();
22036 trigger.onTriggerClick = myTriggerFn;
22037 trigger.applyTo('my-field');
22038 </code></pre>
22039  *
22040  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22041  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22042  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22043  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22044  * @constructor
22045  * Create a new TriggerField.
22046  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22047  * to the base TextField)
22048  */
22049 Roo.form.TriggerField = function(config){
22050     this.mimicing = false;
22051     Roo.form.TriggerField.superclass.constructor.call(this, config);
22052 };
22053
22054 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
22055     /**
22056      * @cfg {String} triggerClass A CSS class to apply to the trigger
22057      */
22058     /**
22059      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22060      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22061      */
22062     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22063     /**
22064      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22065      */
22066     hideTrigger:false,
22067
22068     /** @cfg {Boolean} grow @hide */
22069     /** @cfg {Number} growMin @hide */
22070     /** @cfg {Number} growMax @hide */
22071
22072     /**
22073      * @hide 
22074      * @method
22075      */
22076     autoSize: Roo.emptyFn,
22077     // private
22078     monitorTab : true,
22079     // private
22080     deferHeight : true,
22081
22082     
22083     actionMode : 'wrap',
22084     // private
22085     onResize : function(w, h){
22086         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22087         if(typeof w == 'number'){
22088             var x = w - this.trigger.getWidth();
22089             this.el.setWidth(this.adjustWidth('input', x));
22090             this.trigger.setStyle('left', x+'px');
22091         }
22092     },
22093
22094     // private
22095     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22096
22097     // private
22098     getResizeEl : function(){
22099         return this.wrap;
22100     },
22101
22102     // private
22103     getPositionEl : function(){
22104         return this.wrap;
22105     },
22106
22107     // private
22108     alignErrorIcon : function(){
22109         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22110     },
22111
22112     // private
22113     onRender : function(ct, position){
22114         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22115         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22116         this.trigger = this.wrap.createChild(this.triggerConfig ||
22117                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22118         if(this.hideTrigger){
22119             this.trigger.setDisplayed(false);
22120         }
22121         this.initTrigger();
22122         if(!this.width){
22123             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22124         }
22125     },
22126
22127     // private
22128     initTrigger : function(){
22129         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22130         this.trigger.addClassOnOver('x-form-trigger-over');
22131         this.trigger.addClassOnClick('x-form-trigger-click');
22132     },
22133
22134     // private
22135     onDestroy : function(){
22136         if(this.trigger){
22137             this.trigger.removeAllListeners();
22138             this.trigger.remove();
22139         }
22140         if(this.wrap){
22141             this.wrap.remove();
22142         }
22143         Roo.form.TriggerField.superclass.onDestroy.call(this);
22144     },
22145
22146     // private
22147     onFocus : function(){
22148         Roo.form.TriggerField.superclass.onFocus.call(this);
22149         if(!this.mimicing){
22150             this.wrap.addClass('x-trigger-wrap-focus');
22151             this.mimicing = true;
22152             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22153             if(this.monitorTab){
22154                 this.el.on("keydown", this.checkTab, this);
22155             }
22156         }
22157     },
22158
22159     // private
22160     checkTab : function(e){
22161         if(e.getKey() == e.TAB){
22162             this.triggerBlur();
22163         }
22164     },
22165
22166     // private
22167     onBlur : function(){
22168         // do nothing
22169     },
22170
22171     // private
22172     mimicBlur : function(e, t){
22173         if(!this.wrap.contains(t) && this.validateBlur()){
22174             this.triggerBlur();
22175         }
22176     },
22177
22178     // private
22179     triggerBlur : function(){
22180         this.mimicing = false;
22181         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22182         if(this.monitorTab){
22183             this.el.un("keydown", this.checkTab, this);
22184         }
22185         this.wrap.removeClass('x-trigger-wrap-focus');
22186         Roo.form.TriggerField.superclass.onBlur.call(this);
22187     },
22188
22189     // private
22190     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22191     validateBlur : function(e, t){
22192         return true;
22193     },
22194
22195     // private
22196     onDisable : function(){
22197         Roo.form.TriggerField.superclass.onDisable.call(this);
22198         if(this.wrap){
22199             this.wrap.addClass('x-item-disabled');
22200         }
22201     },
22202
22203     // private
22204     onEnable : function(){
22205         Roo.form.TriggerField.superclass.onEnable.call(this);
22206         if(this.wrap){
22207             this.wrap.removeClass('x-item-disabled');
22208         }
22209     },
22210
22211     // private
22212     onShow : function(){
22213         var ae = this.getActionEl();
22214         
22215         if(ae){
22216             ae.dom.style.display = '';
22217             ae.dom.style.visibility = 'visible';
22218         }
22219     },
22220
22221     // private
22222     
22223     onHide : function(){
22224         var ae = this.getActionEl();
22225         ae.dom.style.display = 'none';
22226     },
22227
22228     /**
22229      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22230      * by an implementing function.
22231      * @method
22232      * @param {EventObject} e
22233      */
22234     onTriggerClick : Roo.emptyFn
22235 });
22236
22237 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22238 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22239 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22240 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22241     initComponent : function(){
22242         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22243
22244         this.triggerConfig = {
22245             tag:'span', cls:'x-form-twin-triggers', cn:[
22246             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22247             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22248         ]};
22249     },
22250
22251     getTrigger : function(index){
22252         return this.triggers[index];
22253     },
22254
22255     initTrigger : function(){
22256         var ts = this.trigger.select('.x-form-trigger', true);
22257         this.wrap.setStyle('overflow', 'hidden');
22258         var triggerField = this;
22259         ts.each(function(t, all, index){
22260             t.hide = function(){
22261                 var w = triggerField.wrap.getWidth();
22262                 this.dom.style.display = 'none';
22263                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22264             };
22265             t.show = function(){
22266                 var w = triggerField.wrap.getWidth();
22267                 this.dom.style.display = '';
22268                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22269             };
22270             var triggerIndex = 'Trigger'+(index+1);
22271
22272             if(this['hide'+triggerIndex]){
22273                 t.dom.style.display = 'none';
22274             }
22275             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22276             t.addClassOnOver('x-form-trigger-over');
22277             t.addClassOnClick('x-form-trigger-click');
22278         }, this);
22279         this.triggers = ts.elements;
22280     },
22281
22282     onTrigger1Click : Roo.emptyFn,
22283     onTrigger2Click : Roo.emptyFn
22284 });/*
22285  * Based on:
22286  * Ext JS Library 1.1.1
22287  * Copyright(c) 2006-2007, Ext JS, LLC.
22288  *
22289  * Originally Released Under LGPL - original licence link has changed is not relivant.
22290  *
22291  * Fork - LGPL
22292  * <script type="text/javascript">
22293  */
22294  
22295 /**
22296  * @class Roo.form.TextArea
22297  * @extends Roo.form.TextField
22298  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22299  * support for auto-sizing.
22300  * @constructor
22301  * Creates a new TextArea
22302  * @param {Object} config Configuration options
22303  */
22304 Roo.form.TextArea = function(config){
22305     Roo.form.TextArea.superclass.constructor.call(this, config);
22306     // these are provided exchanges for backwards compat
22307     // minHeight/maxHeight were replaced by growMin/growMax to be
22308     // compatible with TextField growing config values
22309     if(this.minHeight !== undefined){
22310         this.growMin = this.minHeight;
22311     }
22312     if(this.maxHeight !== undefined){
22313         this.growMax = this.maxHeight;
22314     }
22315 };
22316
22317 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22318     /**
22319      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22320      */
22321     growMin : 60,
22322     /**
22323      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22324      */
22325     growMax: 1000,
22326     /**
22327      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22328      * in the field (equivalent to setting overflow: hidden, defaults to false)
22329      */
22330     preventScrollbars: false,
22331     /**
22332      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22333      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22334      */
22335
22336     // private
22337     onRender : function(ct, position){
22338         if(!this.el){
22339             this.defaultAutoCreate = {
22340                 tag: "textarea",
22341                 style:"width:300px;height:60px;",
22342                 autocomplete: "off"
22343             };
22344         }
22345         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22346         if(this.grow){
22347             this.textSizeEl = Roo.DomHelper.append(document.body, {
22348                 tag: "pre", cls: "x-form-grow-sizer"
22349             });
22350             if(this.preventScrollbars){
22351                 this.el.setStyle("overflow", "hidden");
22352             }
22353             this.el.setHeight(this.growMin);
22354         }
22355     },
22356
22357     onDestroy : function(){
22358         if(this.textSizeEl){
22359             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22360         }
22361         Roo.form.TextArea.superclass.onDestroy.call(this);
22362     },
22363
22364     // private
22365     onKeyUp : function(e){
22366         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22367             this.autoSize();
22368         }
22369     },
22370
22371     /**
22372      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22373      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22374      */
22375     autoSize : function(){
22376         if(!this.grow || !this.textSizeEl){
22377             return;
22378         }
22379         var el = this.el;
22380         var v = el.dom.value;
22381         var ts = this.textSizeEl;
22382
22383         ts.innerHTML = '';
22384         ts.appendChild(document.createTextNode(v));
22385         v = ts.innerHTML;
22386
22387         Roo.fly(ts).setWidth(this.el.getWidth());
22388         if(v.length < 1){
22389             v = "&#160;&#160;";
22390         }else{
22391             if(Roo.isIE){
22392                 v = v.replace(/\n/g, '<p>&#160;</p>');
22393             }
22394             v += "&#160;\n&#160;";
22395         }
22396         ts.innerHTML = v;
22397         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22398         if(h != this.lastHeight){
22399             this.lastHeight = h;
22400             this.el.setHeight(h);
22401             this.fireEvent("autosize", this, h);
22402         }
22403     }
22404 });/*
22405  * Based on:
22406  * Ext JS Library 1.1.1
22407  * Copyright(c) 2006-2007, Ext JS, LLC.
22408  *
22409  * Originally Released Under LGPL - original licence link has changed is not relivant.
22410  *
22411  * Fork - LGPL
22412  * <script type="text/javascript">
22413  */
22414  
22415
22416 /**
22417  * @class Roo.form.NumberField
22418  * @extends Roo.form.TextField
22419  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22420  * @constructor
22421  * Creates a new NumberField
22422  * @param {Object} config Configuration options
22423  */
22424 Roo.form.NumberField = function(config){
22425     Roo.form.NumberField.superclass.constructor.call(this, config);
22426 };
22427
22428 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22429     /**
22430      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22431      */
22432     fieldClass: "x-form-field x-form-num-field",
22433     /**
22434      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22435      */
22436     allowDecimals : true,
22437     /**
22438      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22439      */
22440     decimalSeparator : ".",
22441     /**
22442      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22443      */
22444     decimalPrecision : 2,
22445     /**
22446      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22447      */
22448     allowNegative : true,
22449     /**
22450      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22451      */
22452     minValue : Number.NEGATIVE_INFINITY,
22453     /**
22454      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22455      */
22456     maxValue : Number.MAX_VALUE,
22457     /**
22458      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22459      */
22460     minText : "The minimum value for this field is {0}",
22461     /**
22462      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22463      */
22464     maxText : "The maximum value for this field is {0}",
22465     /**
22466      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22467      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22468      */
22469     nanText : "{0} is not a valid number",
22470
22471     // private
22472     initEvents : function(){
22473         Roo.form.NumberField.superclass.initEvents.call(this);
22474         var allowed = "0123456789";
22475         if(this.allowDecimals){
22476             allowed += this.decimalSeparator;
22477         }
22478         if(this.allowNegative){
22479             allowed += "-";
22480         }
22481         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22482         var keyPress = function(e){
22483             var k = e.getKey();
22484             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22485                 return;
22486             }
22487             var c = e.getCharCode();
22488             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22489                 e.stopEvent();
22490             }
22491         };
22492         this.el.on("keypress", keyPress, this);
22493     },
22494
22495     // private
22496     validateValue : function(value){
22497         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22498             return false;
22499         }
22500         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22501              return true;
22502         }
22503         var num = this.parseValue(value);
22504         if(isNaN(num)){
22505             this.markInvalid(String.format(this.nanText, value));
22506             return false;
22507         }
22508         if(num < this.minValue){
22509             this.markInvalid(String.format(this.minText, this.minValue));
22510             return false;
22511         }
22512         if(num > this.maxValue){
22513             this.markInvalid(String.format(this.maxText, this.maxValue));
22514             return false;
22515         }
22516         return true;
22517     },
22518
22519     getValue : function(){
22520         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22521     },
22522
22523     // private
22524     parseValue : function(value){
22525         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22526         return isNaN(value) ? '' : value;
22527     },
22528
22529     // private
22530     fixPrecision : function(value){
22531         var nan = isNaN(value);
22532         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22533             return nan ? '' : value;
22534         }
22535         return parseFloat(value).toFixed(this.decimalPrecision);
22536     },
22537
22538     setValue : function(v){
22539         v = this.fixPrecision(v);
22540         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22541     },
22542
22543     // private
22544     decimalPrecisionFcn : function(v){
22545         return Math.floor(v);
22546     },
22547
22548     beforeBlur : function(){
22549         var v = this.parseValue(this.getRawValue());
22550         if(v){
22551             this.setValue(v);
22552         }
22553     }
22554 });/*
22555  * Based on:
22556  * Ext JS Library 1.1.1
22557  * Copyright(c) 2006-2007, Ext JS, LLC.
22558  *
22559  * Originally Released Under LGPL - original licence link has changed is not relivant.
22560  *
22561  * Fork - LGPL
22562  * <script type="text/javascript">
22563  */
22564  
22565 /**
22566  * @class Roo.form.DateField
22567  * @extends Roo.form.TriggerField
22568  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22569 * @constructor
22570 * Create a new DateField
22571 * @param {Object} config
22572  */
22573 Roo.form.DateField = function(config){
22574     Roo.form.DateField.superclass.constructor.call(this, config);
22575     
22576       this.addEvents({
22577          
22578         /**
22579          * @event select
22580          * Fires when a date is selected
22581              * @param {Roo.form.DateField} combo This combo box
22582              * @param {Date} date The date selected
22583              */
22584         'select' : true
22585          
22586     });
22587     
22588     
22589     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22590     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22591     this.ddMatch = null;
22592     if(this.disabledDates){
22593         var dd = this.disabledDates;
22594         var re = "(?:";
22595         for(var i = 0; i < dd.length; i++){
22596             re += dd[i];
22597             if(i != dd.length-1) re += "|";
22598         }
22599         this.ddMatch = new RegExp(re + ")");
22600     }
22601 };
22602
22603 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22604     /**
22605      * @cfg {String} format
22606      * The default date format string which can be overriden for localization support.  The format must be
22607      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22608      */
22609     format : "m/d/y",
22610     /**
22611      * @cfg {String} altFormats
22612      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22613      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22614      */
22615     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22616     /**
22617      * @cfg {Array} disabledDays
22618      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22619      */
22620     disabledDays : null,
22621     /**
22622      * @cfg {String} disabledDaysText
22623      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22624      */
22625     disabledDaysText : "Disabled",
22626     /**
22627      * @cfg {Array} disabledDates
22628      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22629      * expression so they are very powerful. Some examples:
22630      * <ul>
22631      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22632      * <li>["03/08", "09/16"] would disable those days for every year</li>
22633      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22634      * <li>["03/../2006"] would disable every day in March 2006</li>
22635      * <li>["^03"] would disable every day in every March</li>
22636      * </ul>
22637      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22638      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22639      */
22640     disabledDates : null,
22641     /**
22642      * @cfg {String} disabledDatesText
22643      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22644      */
22645     disabledDatesText : "Disabled",
22646     /**
22647      * @cfg {Date/String} minValue
22648      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22649      * valid format (defaults to null).
22650      */
22651     minValue : null,
22652     /**
22653      * @cfg {Date/String} maxValue
22654      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22655      * valid format (defaults to null).
22656      */
22657     maxValue : null,
22658     /**
22659      * @cfg {String} minText
22660      * The error text to display when the date in the cell is before minValue (defaults to
22661      * 'The date in this field must be after {minValue}').
22662      */
22663     minText : "The date in this field must be equal to or after {0}",
22664     /**
22665      * @cfg {String} maxText
22666      * The error text to display when the date in the cell is after maxValue (defaults to
22667      * 'The date in this field must be before {maxValue}').
22668      */
22669     maxText : "The date in this field must be equal to or before {0}",
22670     /**
22671      * @cfg {String} invalidText
22672      * The error text to display when the date in the field is invalid (defaults to
22673      * '{value} is not a valid date - it must be in the format {format}').
22674      */
22675     invalidText : "{0} is not a valid date - it must be in the format {1}",
22676     /**
22677      * @cfg {String} triggerClass
22678      * An additional CSS class used to style the trigger button.  The trigger will always get the
22679      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22680      * which displays a calendar icon).
22681      */
22682     triggerClass : 'x-form-date-trigger',
22683     
22684
22685     /**
22686      * @cfg {Boolean} useIso
22687      * if enabled, then the date field will use a hidden field to store the 
22688      * real value as iso formated date. default (false)
22689      */ 
22690     useIso : false,
22691     /**
22692      * @cfg {String/Object} autoCreate
22693      * A DomHelper element spec, or true for a default element spec (defaults to
22694      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22695      */ 
22696     // private
22697     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22698     
22699     // private
22700     hiddenField: false,
22701     
22702     onRender : function(ct, position)
22703     {
22704         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22705         if (this.useIso) {
22706             this.el.dom.removeAttribute('name'); 
22707             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22708                     'before', true);
22709             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22710             // prevent input submission
22711             this.hiddenName = this.name;
22712         }
22713             
22714             
22715     },
22716     
22717     // private
22718     validateValue : function(value)
22719     {
22720         value = this.formatDate(value);
22721         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22722             return false;
22723         }
22724         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22725              return true;
22726         }
22727         var svalue = value;
22728         value = this.parseDate(value);
22729         if(!value){
22730             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22731             return false;
22732         }
22733         var time = value.getTime();
22734         if(this.minValue && time < this.minValue.getTime()){
22735             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22736             return false;
22737         }
22738         if(this.maxValue && time > this.maxValue.getTime()){
22739             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22740             return false;
22741         }
22742         if(this.disabledDays){
22743             var day = value.getDay();
22744             for(var i = 0; i < this.disabledDays.length; i++) {
22745                 if(day === this.disabledDays[i]){
22746                     this.markInvalid(this.disabledDaysText);
22747                     return false;
22748                 }
22749             }
22750         }
22751         var fvalue = this.formatDate(value);
22752         if(this.ddMatch && this.ddMatch.test(fvalue)){
22753             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22754             return false;
22755         }
22756         return true;
22757     },
22758
22759     // private
22760     // Provides logic to override the default TriggerField.validateBlur which just returns true
22761     validateBlur : function(){
22762         return !this.menu || !this.menu.isVisible();
22763     },
22764
22765     /**
22766      * Returns the current date value of the date field.
22767      * @return {Date} The date value
22768      */
22769     getValue : function(){
22770         
22771         return  this.hiddenField ?
22772                 this.hiddenField.value :
22773                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22774     },
22775
22776     /**
22777      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22778      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22779      * (the default format used is "m/d/y").
22780      * <br />Usage:
22781      * <pre><code>
22782 //All of these calls set the same date value (May 4, 2006)
22783
22784 //Pass a date object:
22785 var dt = new Date('5/4/06');
22786 dateField.setValue(dt);
22787
22788 //Pass a date string (default format):
22789 dateField.setValue('5/4/06');
22790
22791 //Pass a date string (custom format):
22792 dateField.format = 'Y-m-d';
22793 dateField.setValue('2006-5-4');
22794 </code></pre>
22795      * @param {String/Date} date The date or valid date string
22796      */
22797     setValue : function(date){
22798         if (this.hiddenField) {
22799             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22800         }
22801         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22802     },
22803
22804     // private
22805     parseDate : function(value){
22806         if(!value || value instanceof Date){
22807             return value;
22808         }
22809         var v = Date.parseDate(value, this.format);
22810         if(!v && this.altFormats){
22811             if(!this.altFormatsArray){
22812                 this.altFormatsArray = this.altFormats.split("|");
22813             }
22814             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22815                 v = Date.parseDate(value, this.altFormatsArray[i]);
22816             }
22817         }
22818         return v;
22819     },
22820
22821     // private
22822     formatDate : function(date, fmt){
22823         return (!date || !(date instanceof Date)) ?
22824                date : date.dateFormat(fmt || this.format);
22825     },
22826
22827     // private
22828     menuListeners : {
22829         select: function(m, d){
22830             this.setValue(d);
22831             this.fireEvent('select', this, d);
22832         },
22833         show : function(){ // retain focus styling
22834             this.onFocus();
22835         },
22836         hide : function(){
22837             this.focus.defer(10, this);
22838             var ml = this.menuListeners;
22839             this.menu.un("select", ml.select,  this);
22840             this.menu.un("show", ml.show,  this);
22841             this.menu.un("hide", ml.hide,  this);
22842         }
22843     },
22844
22845     // private
22846     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22847     onTriggerClick : function(){
22848         if(this.disabled){
22849             return;
22850         }
22851         if(this.menu == null){
22852             this.menu = new Roo.menu.DateMenu();
22853         }
22854         Roo.apply(this.menu.picker,  {
22855             showClear: this.allowBlank,
22856             minDate : this.minValue,
22857             maxDate : this.maxValue,
22858             disabledDatesRE : this.ddMatch,
22859             disabledDatesText : this.disabledDatesText,
22860             disabledDays : this.disabledDays,
22861             disabledDaysText : this.disabledDaysText,
22862             format : this.format,
22863             minText : String.format(this.minText, this.formatDate(this.minValue)),
22864             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22865         });
22866         this.menu.on(Roo.apply({}, this.menuListeners, {
22867             scope:this
22868         }));
22869         this.menu.picker.setValue(this.getValue() || new Date());
22870         this.menu.show(this.el, "tl-bl?");
22871     },
22872
22873     beforeBlur : function(){
22874         var v = this.parseDate(this.getRawValue());
22875         if(v){
22876             this.setValue(v);
22877         }
22878     }
22879
22880     /** @cfg {Boolean} grow @hide */
22881     /** @cfg {Number} growMin @hide */
22882     /** @cfg {Number} growMax @hide */
22883     /**
22884      * @hide
22885      * @method autoSize
22886      */
22887 });/*
22888  * Based on:
22889  * Ext JS Library 1.1.1
22890  * Copyright(c) 2006-2007, Ext JS, LLC.
22891  *
22892  * Originally Released Under LGPL - original licence link has changed is not relivant.
22893  *
22894  * Fork - LGPL
22895  * <script type="text/javascript">
22896  */
22897  
22898 /**
22899  * @class Roo.form.MonthField
22900  * @extends Roo.form.TriggerField
22901  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22902 * @constructor
22903 * Create a new MonthField
22904 * @param {Object} config
22905  */
22906 Roo.form.MonthField = function(config){
22907     
22908     Roo.form.MonthField.superclass.constructor.call(this, config);
22909     
22910       this.addEvents({
22911          
22912         /**
22913          * @event select
22914          * Fires when a date is selected
22915              * @param {Roo.form.MonthFieeld} combo This combo box
22916              * @param {Date} date The date selected
22917              */
22918         'select' : true
22919          
22920     });
22921     
22922     
22923     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22924     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22925     this.ddMatch = null;
22926     if(this.disabledDates){
22927         var dd = this.disabledDates;
22928         var re = "(?:";
22929         for(var i = 0; i < dd.length; i++){
22930             re += dd[i];
22931             if(i != dd.length-1) re += "|";
22932         }
22933         this.ddMatch = new RegExp(re + ")");
22934     }
22935 };
22936
22937 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
22938     /**
22939      * @cfg {String} format
22940      * The default date format string which can be overriden for localization support.  The format must be
22941      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22942      */
22943     format : "M-Y",
22944     /**
22945      * @cfg {String} altFormats
22946      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22947      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22948      */
22949     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22950     /**
22951      * @cfg {Array} disabledDays
22952      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22953      */
22954     disabledDays : [0,1,2,3,4,5,6],
22955     /**
22956      * @cfg {String} disabledDaysText
22957      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22958      */
22959     disabledDaysText : "Disabled",
22960     /**
22961      * @cfg {Array} disabledDates
22962      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22963      * expression so they are very powerful. Some examples:
22964      * <ul>
22965      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22966      * <li>["03/08", "09/16"] would disable those days for every year</li>
22967      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22968      * <li>["03/../2006"] would disable every day in March 2006</li>
22969      * <li>["^03"] would disable every day in every March</li>
22970      * </ul>
22971      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22972      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22973      */
22974     disabledDates : null,
22975     /**
22976      * @cfg {String} disabledDatesText
22977      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22978      */
22979     disabledDatesText : "Disabled",
22980     /**
22981      * @cfg {Date/String} minValue
22982      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22983      * valid format (defaults to null).
22984      */
22985     minValue : null,
22986     /**
22987      * @cfg {Date/String} maxValue
22988      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22989      * valid format (defaults to null).
22990      */
22991     maxValue : null,
22992     /**
22993      * @cfg {String} minText
22994      * The error text to display when the date in the cell is before minValue (defaults to
22995      * 'The date in this field must be after {minValue}').
22996      */
22997     minText : "The date in this field must be equal to or after {0}",
22998     /**
22999      * @cfg {String} maxText
23000      * The error text to display when the date in the cell is after maxValue (defaults to
23001      * 'The date in this field must be before {maxValue}').
23002      */
23003     maxText : "The date in this field must be equal to or before {0}",
23004     /**
23005      * @cfg {String} invalidText
23006      * The error text to display when the date in the field is invalid (defaults to
23007      * '{value} is not a valid date - it must be in the format {format}').
23008      */
23009     invalidText : "{0} is not a valid date - it must be in the format {1}",
23010     /**
23011      * @cfg {String} triggerClass
23012      * An additional CSS class used to style the trigger button.  The trigger will always get the
23013      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
23014      * which displays a calendar icon).
23015      */
23016     triggerClass : 'x-form-date-trigger',
23017     
23018
23019     /**
23020      * @cfg {Boolean} useIso
23021      * if enabled, then the date field will use a hidden field to store the 
23022      * real value as iso formated date. default (true)
23023      */ 
23024     useIso : true,
23025     /**
23026      * @cfg {String/Object} autoCreate
23027      * A DomHelper element spec, or true for a default element spec (defaults to
23028      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
23029      */ 
23030     // private
23031     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
23032     
23033     // private
23034     hiddenField: false,
23035     
23036     hideMonthPicker : false,
23037     
23038     onRender : function(ct, position)
23039     {
23040         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
23041         if (this.useIso) {
23042             this.el.dom.removeAttribute('name'); 
23043             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
23044                     'before', true);
23045             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
23046             // prevent input submission
23047             this.hiddenName = this.name;
23048         }
23049             
23050             
23051     },
23052     
23053     // private
23054     validateValue : function(value)
23055     {
23056         value = this.formatDate(value);
23057         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
23058             return false;
23059         }
23060         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
23061              return true;
23062         }
23063         var svalue = value;
23064         value = this.parseDate(value);
23065         if(!value){
23066             this.markInvalid(String.format(this.invalidText, svalue, this.format));
23067             return false;
23068         }
23069         var time = value.getTime();
23070         if(this.minValue && time < this.minValue.getTime()){
23071             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
23072             return false;
23073         }
23074         if(this.maxValue && time > this.maxValue.getTime()){
23075             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
23076             return false;
23077         }
23078         if(this.disabledDays){
23079             var day = value.getDay();
23080             for(var i = 0; i < this.disabledDays.length; i++) {
23081                 if(day === this.disabledDays[i]){
23082                     this.markInvalid(this.disabledDaysText);
23083                     return false;
23084                 }
23085             }
23086         }
23087         var fvalue = this.formatDate(value);
23088         if(this.ddMatch && this.ddMatch.test(fvalue)){
23089             this.markInvalid(String.format(this.disabledDatesText, fvalue));
23090             return false;
23091         }
23092         return true;
23093     },
23094
23095     // private
23096     // Provides logic to override the default TriggerField.validateBlur which just returns true
23097     validateBlur : function(){
23098         return !this.menu || !this.menu.isVisible();
23099     },
23100
23101     /**
23102      * Returns the current date value of the date field.
23103      * @return {Date} The date value
23104      */
23105     getValue : function(){
23106         
23107         return  this.hiddenField ?
23108                 this.hiddenField.value :
23109                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
23110     },
23111
23112     /**
23113      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
23114      * date, using monthField.format as the date format, according to the same rules as {@link Date#parseDate}
23115      * (the default format used is "m/d/y").
23116      * <br />Usage:
23117      * <pre><code>
23118 //All of these calls set the same date value (May 4, 2006)
23119
23120 //Pass a date object:
23121 var dt = new Date('5/4/06');
23122 monthField.setValue(dt);
23123
23124 //Pass a date string (default format):
23125 monthField.setValue('5/4/06');
23126
23127 //Pass a date string (custom format):
23128 monthField.format = 'Y-m-d';
23129 monthField.setValue('2006-5-4');
23130 </code></pre>
23131      * @param {String/Date} date The date or valid date string
23132      */
23133     setValue : function(date){
23134         if (this.hiddenField) {
23135             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
23136         }
23137         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
23138     },
23139
23140     // private
23141     parseDate : function(value){
23142         if(!value || value instanceof Date){
23143             return value;
23144         }
23145         var v = Date.parseDate(value, this.format);
23146         if(!v && this.altFormats){
23147             if(!this.altFormatsArray){
23148                 this.altFormatsArray = this.altFormats.split("|");
23149             }
23150             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23151                 v = Date.parseDate(value, this.altFormatsArray[i]);
23152             }
23153         }
23154         return v;
23155     },
23156
23157     // private
23158     formatDate : function(date, fmt){
23159         return (!date || !(date instanceof Date)) ?
23160                date : date.dateFormat(fmt || this.format);
23161     },
23162
23163     // private
23164     menuListeners : {
23165         select: function(m, d){
23166             this.setValue(d);
23167             this.fireEvent('select', this, d);
23168         },
23169         show : function(){ // retain focus styling
23170             this.onFocus();
23171         },
23172         hide : function(){
23173             this.focus.defer(10, this);
23174             var ml = this.menuListeners;
23175             this.menu.un("select", ml.select,  this);
23176             this.menu.un("show", ml.show,  this);
23177             this.menu.un("hide", ml.hide,  this);
23178         }
23179     },
23180     // private
23181     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23182     onTriggerClick : function(){
23183         if(this.disabled){
23184             return;
23185         }
23186         if(this.menu == null){
23187             this.menu = new Roo.menu.DateMenu();
23188         }
23189         
23190         Roo.apply(this.menu.picker,  {
23191             
23192             showClear: this.allowBlank,
23193             minDate : this.minValue,
23194             maxDate : this.maxValue,
23195             disabledDatesRE : this.ddMatch,
23196             disabledDatesText : this.disabledDatesText,
23197             
23198             format : this.format,
23199             minText : String.format(this.minText, this.formatDate(this.minValue)),
23200             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23201             
23202         });
23203         
23204         this.menu.on(Roo.apply({}, this.menuListeners, {
23205             scope:this
23206         }));
23207         
23208         var m = this.menu;
23209         var p = m.picker;
23210         p.setValue(this.getValue() || new Date());
23211         m.show(this.el, "tl-bl?");
23212         
23213         // hidden the day picker
23214         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
23215         
23216         (function() {
23217             p.showMonthPicker();
23218         }).defer(100);
23219         
23220         p.hideMonthPicker  = function(disableAnim){
23221             if(this.monthPicker){
23222                 if(disableAnim === true){
23223                     this.monthPicker.hide();
23224                 }else{
23225                     this.monthPicker.slideOut('t', {duration:.2});
23226                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth));
23227                     p.fireEvent("select", this, this.value);
23228                     m.hide();
23229                 }
23230             }
23231         }
23232     },
23233
23234     beforeBlur : function(){
23235         var v = this.parseDate(this.getRawValue());
23236         if(v){
23237             this.setValue(v);
23238         }
23239     }
23240
23241     /** @cfg {Boolean} grow @hide */
23242     /** @cfg {Number} growMin @hide */
23243     /** @cfg {Number} growMax @hide */
23244     /**
23245      * @hide
23246      * @method autoSize
23247      */
23248 });/*
23249  * Based on:
23250  * Ext JS Library 1.1.1
23251  * Copyright(c) 2006-2007, Ext JS, LLC.
23252  *
23253  * Originally Released Under LGPL - original licence link has changed is not relivant.
23254  *
23255  * Fork - LGPL
23256  * <script type="text/javascript">
23257  */
23258  
23259
23260 /**
23261  * @class Roo.form.ComboBox
23262  * @extends Roo.form.TriggerField
23263  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
23264  * @constructor
23265  * Create a new ComboBox.
23266  * @param {Object} config Configuration options
23267  */
23268 Roo.form.ComboBox = function(config){
23269     Roo.form.ComboBox.superclass.constructor.call(this, config);
23270     this.addEvents({
23271         /**
23272          * @event expand
23273          * Fires when the dropdown list is expanded
23274              * @param {Roo.form.ComboBox} combo This combo box
23275              */
23276         'expand' : true,
23277         /**
23278          * @event collapse
23279          * Fires when the dropdown list is collapsed
23280              * @param {Roo.form.ComboBox} combo This combo box
23281              */
23282         'collapse' : true,
23283         /**
23284          * @event beforeselect
23285          * Fires before a list item is selected. Return false to cancel the selection.
23286              * @param {Roo.form.ComboBox} combo This combo box
23287              * @param {Roo.data.Record} record The data record returned from the underlying store
23288              * @param {Number} index The index of the selected item in the dropdown list
23289              */
23290         'beforeselect' : true,
23291         /**
23292          * @event select
23293          * Fires when a list item is selected
23294              * @param {Roo.form.ComboBox} combo This combo box
23295              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
23296              * @param {Number} index The index of the selected item in the dropdown list
23297              */
23298         'select' : true,
23299         /**
23300          * @event beforequery
23301          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
23302          * The event object passed has these properties:
23303              * @param {Roo.form.ComboBox} combo This combo box
23304              * @param {String} query The query
23305              * @param {Boolean} forceAll true to force "all" query
23306              * @param {Boolean} cancel true to cancel the query
23307              * @param {Object} e The query event object
23308              */
23309         'beforequery': true,
23310          /**
23311          * @event add
23312          * Fires when the 'add' icon is pressed (add a listener to enable add button)
23313              * @param {Roo.form.ComboBox} combo This combo box
23314              */
23315         'add' : true,
23316         /**
23317          * @event edit
23318          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
23319              * @param {Roo.form.ComboBox} combo This combo box
23320              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
23321              */
23322         'edit' : true
23323         
23324         
23325     });
23326     if(this.transform){
23327         this.allowDomMove = false;
23328         var s = Roo.getDom(this.transform);
23329         if(!this.hiddenName){
23330             this.hiddenName = s.name;
23331         }
23332         if(!this.store){
23333             this.mode = 'local';
23334             var d = [], opts = s.options;
23335             for(var i = 0, len = opts.length;i < len; i++){
23336                 var o = opts[i];
23337                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
23338                 if(o.selected) {
23339                     this.value = value;
23340                 }
23341                 d.push([value, o.text]);
23342             }
23343             this.store = new Roo.data.SimpleStore({
23344                 'id': 0,
23345                 fields: ['value', 'text'],
23346                 data : d
23347             });
23348             this.valueField = 'value';
23349             this.displayField = 'text';
23350         }
23351         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
23352         if(!this.lazyRender){
23353             this.target = true;
23354             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
23355             s.parentNode.removeChild(s); // remove it
23356             this.render(this.el.parentNode);
23357         }else{
23358             s.parentNode.removeChild(s); // remove it
23359         }
23360
23361     }
23362     if (this.store) {
23363         this.store = Roo.factory(this.store, Roo.data);
23364     }
23365     
23366     this.selectedIndex = -1;
23367     if(this.mode == 'local'){
23368         if(config.queryDelay === undefined){
23369             this.queryDelay = 10;
23370         }
23371         if(config.minChars === undefined){
23372             this.minChars = 0;
23373         }
23374     }
23375 };
23376
23377 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
23378     /**
23379      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23380      */
23381     /**
23382      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23383      * rendering into an Roo.Editor, defaults to false)
23384      */
23385     /**
23386      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23387      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23388      */
23389     /**
23390      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23391      */
23392     /**
23393      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23394      * the dropdown list (defaults to undefined, with no header element)
23395      */
23396
23397      /**
23398      * @cfg {String/Roo.Template} tpl The template to use to render the output
23399      */
23400      
23401     // private
23402     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23403     /**
23404      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23405      */
23406     listWidth: undefined,
23407     /**
23408      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23409      * mode = 'remote' or 'text' if mode = 'local')
23410      */
23411     displayField: undefined,
23412     /**
23413      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23414      * mode = 'remote' or 'value' if mode = 'local'). 
23415      * Note: use of a valueField requires the user make a selection
23416      * in order for a value to be mapped.
23417      */
23418     valueField: undefined,
23419     
23420     
23421     /**
23422      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23423      * field's data value (defaults to the underlying DOM element's name)
23424      */
23425     hiddenName: undefined,
23426     /**
23427      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23428      */
23429     listClass: '',
23430     /**
23431      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23432      */
23433     selectedClass: 'x-combo-selected',
23434     /**
23435      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23436      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23437      * which displays a downward arrow icon).
23438      */
23439     triggerClass : 'x-form-arrow-trigger',
23440     /**
23441      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23442      */
23443     shadow:'sides',
23444     /**
23445      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23446      * anchor positions (defaults to 'tl-bl')
23447      */
23448     listAlign: 'tl-bl?',
23449     /**
23450      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23451      */
23452     maxHeight: 300,
23453     /**
23454      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23455      * query specified by the allQuery config option (defaults to 'query')
23456      */
23457     triggerAction: 'query',
23458     /**
23459      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23460      * (defaults to 4, does not apply if editable = false)
23461      */
23462     minChars : 4,
23463     /**
23464      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23465      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23466      */
23467     typeAhead: false,
23468     /**
23469      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23470      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23471      */
23472     queryDelay: 500,
23473     /**
23474      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23475      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23476      */
23477     pageSize: 0,
23478     /**
23479      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23480      * when editable = true (defaults to false)
23481      */
23482     selectOnFocus:false,
23483     /**
23484      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23485      */
23486     queryParam: 'query',
23487     /**
23488      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23489      * when mode = 'remote' (defaults to 'Loading...')
23490      */
23491     loadingText: 'Loading...',
23492     /**
23493      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23494      */
23495     resizable: false,
23496     /**
23497      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23498      */
23499     handleHeight : 8,
23500     /**
23501      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23502      * traditional select (defaults to true)
23503      */
23504     editable: true,
23505     /**
23506      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23507      */
23508     allQuery: '',
23509     /**
23510      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23511      */
23512     mode: 'remote',
23513     /**
23514      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23515      * listWidth has a higher value)
23516      */
23517     minListWidth : 70,
23518     /**
23519      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23520      * allow the user to set arbitrary text into the field (defaults to false)
23521      */
23522     forceSelection:false,
23523     /**
23524      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23525      * if typeAhead = true (defaults to 250)
23526      */
23527     typeAheadDelay : 250,
23528     /**
23529      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23530      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23531      */
23532     valueNotFoundText : undefined,
23533     /**
23534      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23535      */
23536     blockFocus : false,
23537     
23538     /**
23539      * @cfg {Boolean} disableClear Disable showing of clear button.
23540      */
23541     disableClear : false,
23542     /**
23543      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23544      */
23545     alwaysQuery : false,
23546     
23547     //private
23548     addicon : false,
23549     editicon: false,
23550     
23551     // element that contains real text value.. (when hidden is used..)
23552      
23553     // private
23554     onRender : function(ct, position){
23555         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23556         if(this.hiddenName){
23557             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23558                     'before', true);
23559             this.hiddenField.value =
23560                 this.hiddenValue !== undefined ? this.hiddenValue :
23561                 this.value !== undefined ? this.value : '';
23562
23563             // prevent input submission
23564             this.el.dom.removeAttribute('name');
23565              
23566              
23567         }
23568         if(Roo.isGecko){
23569             this.el.dom.setAttribute('autocomplete', 'off');
23570         }
23571
23572         var cls = 'x-combo-list';
23573
23574         this.list = new Roo.Layer({
23575             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23576         });
23577
23578         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23579         this.list.setWidth(lw);
23580         this.list.swallowEvent('mousewheel');
23581         this.assetHeight = 0;
23582
23583         if(this.title){
23584             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23585             this.assetHeight += this.header.getHeight();
23586         }
23587
23588         this.innerList = this.list.createChild({cls:cls+'-inner'});
23589         this.innerList.on('mouseover', this.onViewOver, this);
23590         this.innerList.on('mousemove', this.onViewMove, this);
23591         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23592         
23593         if(this.allowBlank && !this.pageSize && !this.disableClear){
23594             this.footer = this.list.createChild({cls:cls+'-ft'});
23595             this.pageTb = new Roo.Toolbar(this.footer);
23596            
23597         }
23598         if(this.pageSize){
23599             this.footer = this.list.createChild({cls:cls+'-ft'});
23600             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23601                     {pageSize: this.pageSize});
23602             
23603         }
23604         
23605         if (this.pageTb && this.allowBlank && !this.disableClear) {
23606             var _this = this;
23607             this.pageTb.add(new Roo.Toolbar.Fill(), {
23608                 cls: 'x-btn-icon x-btn-clear',
23609                 text: '&#160;',
23610                 handler: function()
23611                 {
23612                     _this.collapse();
23613                     _this.clearValue();
23614                     _this.onSelect(false, -1);
23615                 }
23616             });
23617         }
23618         if (this.footer) {
23619             this.assetHeight += this.footer.getHeight();
23620         }
23621         
23622
23623         if(!this.tpl){
23624             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23625         }
23626
23627         this.view = new Roo.View(this.innerList, this.tpl, {
23628             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23629         });
23630
23631         this.view.on('click', this.onViewClick, this);
23632
23633         this.store.on('beforeload', this.onBeforeLoad, this);
23634         this.store.on('load', this.onLoad, this);
23635         this.store.on('loadexception', this.onLoadException, this);
23636
23637         if(this.resizable){
23638             this.resizer = new Roo.Resizable(this.list,  {
23639                pinned:true, handles:'se'
23640             });
23641             this.resizer.on('resize', function(r, w, h){
23642                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23643                 this.listWidth = w;
23644                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23645                 this.restrictHeight();
23646             }, this);
23647             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23648         }
23649         if(!this.editable){
23650             this.editable = true;
23651             this.setEditable(false);
23652         }  
23653         
23654         
23655         if (typeof(this.events.add.listeners) != 'undefined') {
23656             
23657             this.addicon = this.wrap.createChild(
23658                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23659        
23660             this.addicon.on('click', function(e) {
23661                 this.fireEvent('add', this);
23662             }, this);
23663         }
23664         if (typeof(this.events.edit.listeners) != 'undefined') {
23665             
23666             this.editicon = this.wrap.createChild(
23667                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23668             if (this.addicon) {
23669                 this.editicon.setStyle('margin-left', '40px');
23670             }
23671             this.editicon.on('click', function(e) {
23672                 
23673                 // we fire even  if inothing is selected..
23674                 this.fireEvent('edit', this, this.lastData );
23675                 
23676             }, this);
23677         }
23678         
23679         
23680         
23681     },
23682
23683     // private
23684     initEvents : function(){
23685         Roo.form.ComboBox.superclass.initEvents.call(this);
23686
23687         this.keyNav = new Roo.KeyNav(this.el, {
23688             "up" : function(e){
23689                 this.inKeyMode = true;
23690                 this.selectPrev();
23691             },
23692
23693             "down" : function(e){
23694                 if(!this.isExpanded()){
23695                     this.onTriggerClick();
23696                 }else{
23697                     this.inKeyMode = true;
23698                     this.selectNext();
23699                 }
23700             },
23701
23702             "enter" : function(e){
23703                 this.onViewClick();
23704                 //return true;
23705             },
23706
23707             "esc" : function(e){
23708                 this.collapse();
23709             },
23710
23711             "tab" : function(e){
23712                 this.onViewClick(false);
23713                 this.fireEvent("specialkey", this, e);
23714                 return true;
23715             },
23716
23717             scope : this,
23718
23719             doRelay : function(foo, bar, hname){
23720                 if(hname == 'down' || this.scope.isExpanded()){
23721                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23722                 }
23723                 return true;
23724             },
23725
23726             forceKeyDown: true
23727         });
23728         this.queryDelay = Math.max(this.queryDelay || 10,
23729                 this.mode == 'local' ? 10 : 250);
23730         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23731         if(this.typeAhead){
23732             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23733         }
23734         if(this.editable !== false){
23735             this.el.on("keyup", this.onKeyUp, this);
23736         }
23737         if(this.forceSelection){
23738             this.on('blur', this.doForce, this);
23739         }
23740     },
23741
23742     onDestroy : function(){
23743         if(this.view){
23744             this.view.setStore(null);
23745             this.view.el.removeAllListeners();
23746             this.view.el.remove();
23747             this.view.purgeListeners();
23748         }
23749         if(this.list){
23750             this.list.destroy();
23751         }
23752         if(this.store){
23753             this.store.un('beforeload', this.onBeforeLoad, this);
23754             this.store.un('load', this.onLoad, this);
23755             this.store.un('loadexception', this.onLoadException, this);
23756         }
23757         Roo.form.ComboBox.superclass.onDestroy.call(this);
23758     },
23759
23760     // private
23761     fireKey : function(e){
23762         if(e.isNavKeyPress() && !this.list.isVisible()){
23763             this.fireEvent("specialkey", this, e);
23764         }
23765     },
23766
23767     // private
23768     onResize: function(w, h){
23769         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23770         
23771         if(typeof w != 'number'){
23772             // we do not handle it!?!?
23773             return;
23774         }
23775         var tw = this.trigger.getWidth();
23776         tw += this.addicon ? this.addicon.getWidth() : 0;
23777         tw += this.editicon ? this.editicon.getWidth() : 0;
23778         var x = w - tw;
23779         this.el.setWidth( this.adjustWidth('input', x));
23780             
23781         this.trigger.setStyle('left', x+'px');
23782         
23783         if(this.list && this.listWidth === undefined){
23784             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23785             this.list.setWidth(lw);
23786             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23787         }
23788         
23789     
23790         
23791     },
23792
23793     /**
23794      * Allow or prevent the user from directly editing the field text.  If false is passed,
23795      * the user will only be able to select from the items defined in the dropdown list.  This method
23796      * is the runtime equivalent of setting the 'editable' config option at config time.
23797      * @param {Boolean} value True to allow the user to directly edit the field text
23798      */
23799     setEditable : function(value){
23800         if(value == this.editable){
23801             return;
23802         }
23803         this.editable = value;
23804         if(!value){
23805             this.el.dom.setAttribute('readOnly', true);
23806             this.el.on('mousedown', this.onTriggerClick,  this);
23807             this.el.addClass('x-combo-noedit');
23808         }else{
23809             this.el.dom.setAttribute('readOnly', false);
23810             this.el.un('mousedown', this.onTriggerClick,  this);
23811             this.el.removeClass('x-combo-noedit');
23812         }
23813     },
23814
23815     // private
23816     onBeforeLoad : function(){
23817         if(!this.hasFocus){
23818             return;
23819         }
23820         this.innerList.update(this.loadingText ?
23821                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23822         this.restrictHeight();
23823         this.selectedIndex = -1;
23824     },
23825
23826     // private
23827     onLoad : function(){
23828         if(!this.hasFocus){
23829             return;
23830         }
23831         if(this.store.getCount() > 0){
23832             this.expand();
23833             this.restrictHeight();
23834             if(this.lastQuery == this.allQuery){
23835                 if(this.editable){
23836                     this.el.dom.select();
23837                 }
23838                 if(!this.selectByValue(this.value, true)){
23839                     this.select(0, true);
23840                 }
23841             }else{
23842                 this.selectNext();
23843                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23844                     this.taTask.delay(this.typeAheadDelay);
23845                 }
23846             }
23847         }else{
23848             this.onEmptyResults();
23849         }
23850         //this.el.focus();
23851     },
23852     // private
23853     onLoadException : function()
23854     {
23855         this.collapse();
23856         Roo.log(this.store.reader.jsonData);
23857         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23858             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23859         }
23860         
23861         
23862     },
23863     // private
23864     onTypeAhead : function(){
23865         if(this.store.getCount() > 0){
23866             var r = this.store.getAt(0);
23867             var newValue = r.data[this.displayField];
23868             var len = newValue.length;
23869             var selStart = this.getRawValue().length;
23870             if(selStart != len){
23871                 this.setRawValue(newValue);
23872                 this.selectText(selStart, newValue.length);
23873             }
23874         }
23875     },
23876
23877     // private
23878     onSelect : function(record, index){
23879         if(this.fireEvent('beforeselect', this, record, index) !== false){
23880             this.setFromData(index > -1 ? record.data : false);
23881             this.collapse();
23882             this.fireEvent('select', this, record, index);
23883         }
23884     },
23885
23886     /**
23887      * Returns the currently selected field value or empty string if no value is set.
23888      * @return {String} value The selected value
23889      */
23890     getValue : function(){
23891         if(this.valueField){
23892             return typeof this.value != 'undefined' ? this.value : '';
23893         }else{
23894             return Roo.form.ComboBox.superclass.getValue.call(this);
23895         }
23896     },
23897
23898     /**
23899      * Clears any text/value currently set in the field
23900      */
23901     clearValue : function(){
23902         if(this.hiddenField){
23903             this.hiddenField.value = '';
23904         }
23905         this.value = '';
23906         this.setRawValue('');
23907         this.lastSelectionText = '';
23908         this.applyEmptyText();
23909     },
23910
23911     /**
23912      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23913      * will be displayed in the field.  If the value does not match the data value of an existing item,
23914      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23915      * Otherwise the field will be blank (although the value will still be set).
23916      * @param {String} value The value to match
23917      */
23918     setValue : function(v){
23919         var text = v;
23920         if(this.valueField){
23921             var r = this.findRecord(this.valueField, v);
23922             if(r){
23923                 text = r.data[this.displayField];
23924             }else if(this.valueNotFoundText !== undefined){
23925                 text = this.valueNotFoundText;
23926             }
23927         }
23928         this.lastSelectionText = text;
23929         if(this.hiddenField){
23930             this.hiddenField.value = v;
23931         }
23932         Roo.form.ComboBox.superclass.setValue.call(this, text);
23933         this.value = v;
23934     },
23935     /**
23936      * @property {Object} the last set data for the element
23937      */
23938     
23939     lastData : false,
23940     /**
23941      * Sets the value of the field based on a object which is related to the record format for the store.
23942      * @param {Object} value the value to set as. or false on reset?
23943      */
23944     setFromData : function(o){
23945         var dv = ''; // display value
23946         var vv = ''; // value value..
23947         this.lastData = o;
23948         if (this.displayField) {
23949             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23950         } else {
23951             // this is an error condition!!!
23952             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23953         }
23954         
23955         if(this.valueField){
23956             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23957         }
23958         if(this.hiddenField){
23959             this.hiddenField.value = vv;
23960             
23961             this.lastSelectionText = dv;
23962             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23963             this.value = vv;
23964             return;
23965         }
23966         // no hidden field.. - we store the value in 'value', but still display
23967         // display field!!!!
23968         this.lastSelectionText = dv;
23969         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23970         this.value = vv;
23971         
23972         
23973     },
23974     // private
23975     reset : function(){
23976         // overridden so that last data is reset..
23977         this.setValue(this.originalValue);
23978         this.clearInvalid();
23979         this.lastData = false;
23980     },
23981     // private
23982     findRecord : function(prop, value){
23983         var record;
23984         if(this.store.getCount() > 0){
23985             this.store.each(function(r){
23986                 if(r.data[prop] == value){
23987                     record = r;
23988                     return false;
23989                 }
23990                 return true;
23991             });
23992         }
23993         return record;
23994     },
23995     
23996     getName: function()
23997     {
23998         // returns hidden if it's set..
23999         if (!this.rendered) {return ''};
24000         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
24001         
24002     },
24003     // private
24004     onViewMove : function(e, t){
24005         this.inKeyMode = false;
24006     },
24007
24008     // private
24009     onViewOver : function(e, t){
24010         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
24011             return;
24012         }
24013         var item = this.view.findItemFromChild(t);
24014         if(item){
24015             var index = this.view.indexOf(item);
24016             this.select(index, false);
24017         }
24018     },
24019
24020     // private
24021     onViewClick : function(doFocus)
24022     {
24023         var index = this.view.getSelectedIndexes()[0];
24024         var r = this.store.getAt(index);
24025         if(r){
24026             this.onSelect(r, index);
24027         }
24028         if(doFocus !== false && !this.blockFocus){
24029             this.el.focus();
24030         }
24031     },
24032
24033     // private
24034     restrictHeight : function(){
24035         this.innerList.dom.style.height = '';
24036         var inner = this.innerList.dom;
24037         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
24038         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
24039         this.list.beginUpdate();
24040         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
24041         this.list.alignTo(this.el, this.listAlign);
24042         this.list.endUpdate();
24043     },
24044
24045     // private
24046     onEmptyResults : function(){
24047         this.collapse();
24048     },
24049
24050     /**
24051      * Returns true if the dropdown list is expanded, else false.
24052      */
24053     isExpanded : function(){
24054         return this.list.isVisible();
24055     },
24056
24057     /**
24058      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
24059      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24060      * @param {String} value The data value of the item to select
24061      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24062      * selected item if it is not currently in view (defaults to true)
24063      * @return {Boolean} True if the value matched an item in the list, else false
24064      */
24065     selectByValue : function(v, scrollIntoView){
24066         if(v !== undefined && v !== null){
24067             var r = this.findRecord(this.valueField || this.displayField, v);
24068             if(r){
24069                 this.select(this.store.indexOf(r), scrollIntoView);
24070                 return true;
24071             }
24072         }
24073         return false;
24074     },
24075
24076     /**
24077      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
24078      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24079      * @param {Number} index The zero-based index of the list item to select
24080      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24081      * selected item if it is not currently in view (defaults to true)
24082      */
24083     select : function(index, scrollIntoView){
24084         this.selectedIndex = index;
24085         this.view.select(index);
24086         if(scrollIntoView !== false){
24087             var el = this.view.getNode(index);
24088             if(el){
24089                 this.innerList.scrollChildIntoView(el, false);
24090             }
24091         }
24092     },
24093
24094     // private
24095     selectNext : function(){
24096         var ct = this.store.getCount();
24097         if(ct > 0){
24098             if(this.selectedIndex == -1){
24099                 this.select(0);
24100             }else if(this.selectedIndex < ct-1){
24101                 this.select(this.selectedIndex+1);
24102             }
24103         }
24104     },
24105
24106     // private
24107     selectPrev : function(){
24108         var ct = this.store.getCount();
24109         if(ct > 0){
24110             if(this.selectedIndex == -1){
24111                 this.select(0);
24112             }else if(this.selectedIndex != 0){
24113                 this.select(this.selectedIndex-1);
24114             }
24115         }
24116     },
24117
24118     // private
24119     onKeyUp : function(e){
24120         if(this.editable !== false && !e.isSpecialKey()){
24121             this.lastKey = e.getKey();
24122             this.dqTask.delay(this.queryDelay);
24123         }
24124     },
24125
24126     // private
24127     validateBlur : function(){
24128         return !this.list || !this.list.isVisible();   
24129     },
24130
24131     // private
24132     initQuery : function(){
24133         this.doQuery(this.getRawValue());
24134     },
24135
24136     // private
24137     doForce : function(){
24138         if(this.el.dom.value.length > 0){
24139             this.el.dom.value =
24140                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
24141             this.applyEmptyText();
24142         }
24143     },
24144
24145     /**
24146      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
24147      * query allowing the query action to be canceled if needed.
24148      * @param {String} query The SQL query to execute
24149      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
24150      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
24151      * saved in the current store (defaults to false)
24152      */
24153     doQuery : function(q, forceAll){
24154         if(q === undefined || q === null){
24155             q = '';
24156         }
24157         var qe = {
24158             query: q,
24159             forceAll: forceAll,
24160             combo: this,
24161             cancel:false
24162         };
24163         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
24164             return false;
24165         }
24166         q = qe.query;
24167         forceAll = qe.forceAll;
24168         if(forceAll === true || (q.length >= this.minChars)){
24169             if(this.lastQuery != q || this.alwaysQuery){
24170                 this.lastQuery = q;
24171                 if(this.mode == 'local'){
24172                     this.selectedIndex = -1;
24173                     if(forceAll){
24174                         this.store.clearFilter();
24175                     }else{
24176                         this.store.filter(this.displayField, q);
24177                     }
24178                     this.onLoad();
24179                 }else{
24180                     this.store.baseParams[this.queryParam] = q;
24181                     this.store.load({
24182                         params: this.getParams(q)
24183                     });
24184                     this.expand();
24185                 }
24186             }else{
24187                 this.selectedIndex = -1;
24188                 this.onLoad();   
24189             }
24190         }
24191     },
24192
24193     // private
24194     getParams : function(q){
24195         var p = {};
24196         //p[this.queryParam] = q;
24197         if(this.pageSize){
24198             p.start = 0;
24199             p.limit = this.pageSize;
24200         }
24201         return p;
24202     },
24203
24204     /**
24205      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
24206      */
24207     collapse : function(){
24208         if(!this.isExpanded()){
24209             return;
24210         }
24211         this.list.hide();
24212         Roo.get(document).un('mousedown', this.collapseIf, this);
24213         Roo.get(document).un('mousewheel', this.collapseIf, this);
24214         if (!this.editable) {
24215             Roo.get(document).un('keydown', this.listKeyPress, this);
24216         }
24217         this.fireEvent('collapse', this);
24218     },
24219
24220     // private
24221     collapseIf : function(e){
24222         if(!e.within(this.wrap) && !e.within(this.list)){
24223             this.collapse();
24224         }
24225     },
24226
24227     /**
24228      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
24229      */
24230     expand : function(){
24231         if(this.isExpanded() || !this.hasFocus){
24232             return;
24233         }
24234         this.list.alignTo(this.el, this.listAlign);
24235         this.list.show();
24236         Roo.get(document).on('mousedown', this.collapseIf, this);
24237         Roo.get(document).on('mousewheel', this.collapseIf, this);
24238         if (!this.editable) {
24239             Roo.get(document).on('keydown', this.listKeyPress, this);
24240         }
24241         
24242         this.fireEvent('expand', this);
24243     },
24244
24245     // private
24246     // Implements the default empty TriggerField.onTriggerClick function
24247     onTriggerClick : function(){
24248         if(this.disabled){
24249             return;
24250         }
24251         if(this.isExpanded()){
24252             this.collapse();
24253             if (!this.blockFocus) {
24254                 this.el.focus();
24255             }
24256             
24257         }else {
24258             this.hasFocus = true;
24259             if(this.triggerAction == 'all') {
24260                 this.doQuery(this.allQuery, true);
24261             } else {
24262                 this.doQuery(this.getRawValue());
24263             }
24264             if (!this.blockFocus) {
24265                 this.el.focus();
24266             }
24267         }
24268     },
24269     listKeyPress : function(e)
24270     {
24271         //Roo.log('listkeypress');
24272         // scroll to first matching element based on key pres..
24273         if (e.isSpecialKey()) {
24274             return false;
24275         }
24276         var k = String.fromCharCode(e.getKey()).toUpperCase();
24277         //Roo.log(k);
24278         var match  = false;
24279         var csel = this.view.getSelectedNodes();
24280         var cselitem = false;
24281         if (csel.length) {
24282             var ix = this.view.indexOf(csel[0]);
24283             cselitem  = this.store.getAt(ix);
24284             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
24285                 cselitem = false;
24286             }
24287             
24288         }
24289         
24290         this.store.each(function(v) { 
24291             if (cselitem) {
24292                 // start at existing selection.
24293                 if (cselitem.id == v.id) {
24294                     cselitem = false;
24295                 }
24296                 return;
24297             }
24298                 
24299             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
24300                 match = this.store.indexOf(v);
24301                 return false;
24302             }
24303         }, this);
24304         
24305         if (match === false) {
24306             return true; // no more action?
24307         }
24308         // scroll to?
24309         this.view.select(match);
24310         var sn = Roo.get(this.view.getSelectedNodes()[0])
24311         sn.scrollIntoView(sn.dom.parentNode, false);
24312     }
24313
24314     /** 
24315     * @cfg {Boolean} grow 
24316     * @hide 
24317     */
24318     /** 
24319     * @cfg {Number} growMin 
24320     * @hide 
24321     */
24322     /** 
24323     * @cfg {Number} growMax 
24324     * @hide 
24325     */
24326     /**
24327      * @hide
24328      * @method autoSize
24329      */
24330 });/*
24331  * Copyright(c) 2010-2012, Roo J Solutions Limited
24332  *
24333  * Licence LGPL
24334  *
24335  */
24336
24337 /**
24338  * @class Roo.form.ComboBoxArray
24339  * @extends Roo.form.TextField
24340  * A facebook style adder... for lists of email / people / countries  etc...
24341  * pick multiple items from a combo box, and shows each one.
24342  *
24343  *  Fred [x]  Brian [x]  [Pick another |v]
24344  *
24345  *
24346  *  For this to work: it needs various extra information
24347  *    - normal combo problay has
24348  *      name, hiddenName
24349  *    + displayField, valueField
24350  *
24351  *    For our purpose...
24352  *
24353  *
24354  *   If we change from 'extends' to wrapping...
24355  *   
24356  *  
24357  *
24358  
24359  
24360  * @constructor
24361  * Create a new ComboBoxArray.
24362  * @param {Object} config Configuration options
24363  */
24364  
24365
24366 Roo.form.ComboBoxArray = function(config)
24367 {
24368     
24369     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24370     
24371     this.items = new Roo.util.MixedCollection(false);
24372     
24373     // construct the child combo...
24374     
24375     
24376     
24377     
24378    
24379     
24380 }
24381
24382  
24383 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24384
24385     /**
24386      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24387      */
24388     
24389     lastData : false,
24390     
24391     // behavies liek a hiddne field
24392     inputType:      'hidden',
24393     /**
24394      * @cfg {Number} width The width of the box that displays the selected element
24395      */ 
24396     width:          300,
24397
24398     
24399     
24400     /**
24401      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24402      */
24403     name : false,
24404     /**
24405      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24406      */
24407     hiddenName : false,
24408     
24409     
24410     // private the array of items that are displayed..
24411     items  : false,
24412     // private - the hidden field el.
24413     hiddenEl : false,
24414     // private - the filed el..
24415     el : false,
24416     
24417     //validateValue : function() { return true; }, // all values are ok!
24418     //onAddClick: function() { },
24419     
24420     onRender : function(ct, position) 
24421     {
24422         
24423         // create the standard hidden element
24424         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24425         
24426         
24427         // give fake names to child combo;
24428         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24429         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24430         
24431         this.combo = Roo.factory(this.combo, Roo.form);
24432         this.combo.onRender(ct, position);
24433         
24434         // assigned so form know we need to do this..
24435         this.store          = this.combo.store;
24436         this.valueField     = this.combo.valueField;
24437         this.displayField   = this.combo.displayField ;
24438         
24439         
24440         this.combo.wrap.addClass('x-cbarray-grp');
24441         
24442         var cbwrap = this.combo.wrap.createChild(
24443             {tag: 'div', cls: 'x-cbarray-cb'},
24444             this.combo.el.dom
24445         );
24446         
24447              
24448         this.hiddenEl = this.combo.wrap.createChild({
24449             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24450         });
24451         this.el = this.combo.wrap.createChild({
24452             tag: 'input',  type:'hidden' , name: this.name, value : ''
24453         });
24454          //   this.el.dom.removeAttribute("name");
24455         
24456         
24457         this.outerWrap = this.combo.wrap;
24458         this.wrap = cbwrap;
24459         
24460         this.outerWrap.setWidth(this.width);
24461         this.outerWrap.dom.removeChild(this.el.dom);
24462         
24463         this.wrap.dom.appendChild(this.el.dom);
24464         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24465         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24466         
24467         this.combo.trigger.setStyle('position','relative');
24468         this.combo.trigger.setStyle('left', '0px');
24469         this.combo.trigger.setStyle('top', '2px');
24470         
24471         this.combo.el.setStyle('vertical-align', 'text-bottom');
24472         
24473         //this.trigger.setStyle('vertical-align', 'top');
24474         
24475         // this should use the code from combo really... on('add' ....)
24476         if (this.adder) {
24477             
24478         
24479             this.adder = this.outerWrap.createChild(
24480                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24481             var _t = this;
24482             this.adder.on('click', function(e) {
24483                 _t.fireEvent('adderclick', this, e);
24484             }, _t);
24485         }
24486         //var _t = this;
24487         //this.adder.on('click', this.onAddClick, _t);
24488         
24489         
24490         this.combo.on('select', function(cb, rec, ix) {
24491             this.addItem(rec.data);
24492             
24493             cb.setValue('');
24494             cb.el.dom.value = '';
24495             //cb.lastData = rec.data;
24496             // add to list
24497             
24498         }, this);
24499         
24500         
24501     },
24502     
24503     
24504     getName: function()
24505     {
24506         // returns hidden if it's set..
24507         if (!this.rendered) {return ''};
24508         return  this.hiddenName ? this.hiddenName : this.name;
24509         
24510     },
24511     
24512     
24513     onResize: function(w, h){
24514         
24515         return;
24516         // not sure if this is needed..
24517         //this.combo.onResize(w,h);
24518         
24519         if(typeof w != 'number'){
24520             // we do not handle it!?!?
24521             return;
24522         }
24523         var tw = this.combo.trigger.getWidth();
24524         tw += this.addicon ? this.addicon.getWidth() : 0;
24525         tw += this.editicon ? this.editicon.getWidth() : 0;
24526         var x = w - tw;
24527         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24528             
24529         this.combo.trigger.setStyle('left', '0px');
24530         
24531         if(this.list && this.listWidth === undefined){
24532             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24533             this.list.setWidth(lw);
24534             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24535         }
24536         
24537     
24538         
24539     },
24540     
24541     addItem: function(rec)
24542     {
24543         var valueField = this.combo.valueField;
24544         var displayField = this.combo.displayField;
24545         if (this.items.indexOfKey(rec[valueField]) > -1) {
24546             //console.log("GOT " + rec.data.id);
24547             return;
24548         }
24549         
24550         var x = new Roo.form.ComboBoxArray.Item({
24551             //id : rec[this.idField],
24552             data : rec,
24553             displayField : displayField ,
24554             tipField : displayField ,
24555             cb : this
24556         });
24557         // use the 
24558         this.items.add(rec[valueField],x);
24559         // add it before the element..
24560         this.updateHiddenEl();
24561         x.render(this.outerWrap, this.wrap.dom);
24562         // add the image handler..
24563     },
24564     
24565     updateHiddenEl : function()
24566     {
24567         this.validate();
24568         if (!this.hiddenEl) {
24569             return;
24570         }
24571         var ar = [];
24572         var idField = this.combo.valueField;
24573         
24574         this.items.each(function(f) {
24575             ar.push(f.data[idField]);
24576            
24577         });
24578         this.hiddenEl.dom.value = ar.join(',');
24579         this.validate();
24580     },
24581     
24582     reset : function()
24583     {
24584         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24585         this.items.each(function(f) {
24586            f.remove(); 
24587         });
24588         this.el.dom.value = '';
24589         if (this.hiddenEl) {
24590             this.hiddenEl.dom.value = '';
24591         }
24592         
24593     },
24594     getValue: function()
24595     {
24596         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24597     },
24598     setValue: function(v) // not a valid action - must use addItems..
24599     {
24600          
24601         this.reset();
24602         
24603         
24604         
24605         if (this.store.isLocal && (typeof(v) == 'string')) {
24606             // then we can use the store to find the values..
24607             // comma seperated at present.. this needs to allow JSON based encoding..
24608             this.hiddenEl.value  = v;
24609             var v_ar = [];
24610             Roo.each(v.split(','), function(k) {
24611                 Roo.log("CHECK " + this.valueField + ',' + k);
24612                 var li = this.store.query(this.valueField, k);
24613                 if (!li.length) {
24614                     return;
24615                 }
24616                 add = {};
24617                 add[this.valueField] = k;
24618                 add[this.displayField] = li.item(0).data[this.displayField];
24619                 
24620                 this.addItem(add);
24621             }, this) 
24622              
24623         }
24624         if (typeof(v) == 'object') {
24625             // then let's assume it's an array of objects..
24626             Roo.each(v, function(l) {
24627                 this.addItem(l);
24628             }, this);
24629              
24630         }
24631         
24632         
24633     },
24634     setFromData: function(v)
24635     {
24636         // this recieves an object, if setValues is called.
24637         this.reset();
24638         this.el.dom.value = v[this.displayField];
24639         this.hiddenEl.dom.value = v[this.valueField];
24640         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24641             return;
24642         }
24643         var kv = v[this.valueField];
24644         var dv = v[this.displayField];
24645         kv = typeof(kv) != 'string' ? '' : kv;
24646         dv = typeof(dv) != 'string' ? '' : dv;
24647         
24648         
24649         var keys = kv.split(',');
24650         var display = dv.split(',');
24651         for (var i = 0 ; i < keys.length; i++) {
24652             
24653             add = {};
24654             add[this.valueField] = keys[i];
24655             add[this.displayField] = display[i];
24656             this.addItem(add);
24657         }
24658       
24659         
24660     },
24661     
24662     
24663     validateValue : function(value){
24664         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24665         
24666     }
24667     
24668 });
24669
24670
24671
24672 /**
24673  * @class Roo.form.ComboBoxArray.Item
24674  * @extends Roo.BoxComponent
24675  * A selected item in the list
24676  *  Fred [x]  Brian [x]  [Pick another |v]
24677  * 
24678  * @constructor
24679  * Create a new item.
24680  * @param {Object} config Configuration options
24681  */
24682  
24683 Roo.form.ComboBoxArray.Item = function(config) {
24684     config.id = Roo.id();
24685     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24686 }
24687
24688 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24689     data : {},
24690     cb: false,
24691     displayField : false,
24692     tipField : false,
24693     
24694     
24695     defaultAutoCreate : {
24696         tag: 'div',
24697         cls: 'x-cbarray-item',
24698         cn : [ 
24699             { tag: 'div' },
24700             {
24701                 tag: 'img',
24702                 width:16,
24703                 height : 16,
24704                 src : Roo.BLANK_IMAGE_URL ,
24705                 align: 'center'
24706             }
24707         ]
24708         
24709     },
24710     
24711  
24712     onRender : function(ct, position)
24713     {
24714         Roo.form.Field.superclass.onRender.call(this, ct, position);
24715         
24716         if(!this.el){
24717             var cfg = this.getAutoCreate();
24718             this.el = ct.createChild(cfg, position);
24719         }
24720         
24721         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24722         
24723         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24724             this.cb.renderer(this.data) :
24725             String.format('{0}',this.data[this.displayField]);
24726         
24727             
24728         this.el.child('div').dom.setAttribute('qtip',
24729                         String.format('{0}',this.data[this.tipField])
24730         );
24731         
24732         this.el.child('img').on('click', this.remove, this);
24733         
24734     },
24735    
24736     remove : function()
24737     {
24738         
24739         this.cb.items.remove(this);
24740         this.el.child('img').un('click', this.remove, this);
24741         this.el.remove();
24742         this.cb.updateHiddenEl();
24743     }
24744     
24745     
24746 });/*
24747  * Based on:
24748  * Ext JS Library 1.1.1
24749  * Copyright(c) 2006-2007, Ext JS, LLC.
24750  *
24751  * Originally Released Under LGPL - original licence link has changed is not relivant.
24752  *
24753  * Fork - LGPL
24754  * <script type="text/javascript">
24755  */
24756 /**
24757  * @class Roo.form.Checkbox
24758  * @extends Roo.form.Field
24759  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24760  * @constructor
24761  * Creates a new Checkbox
24762  * @param {Object} config Configuration options
24763  */
24764 Roo.form.Checkbox = function(config){
24765     Roo.form.Checkbox.superclass.constructor.call(this, config);
24766     this.addEvents({
24767         /**
24768          * @event check
24769          * Fires when the checkbox is checked or unchecked.
24770              * @param {Roo.form.Checkbox} this This checkbox
24771              * @param {Boolean} checked The new checked value
24772              */
24773         check : true
24774     });
24775 };
24776
24777 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24778     /**
24779      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24780      */
24781     focusClass : undefined,
24782     /**
24783      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24784      */
24785     fieldClass: "x-form-field",
24786     /**
24787      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24788      */
24789     checked: false,
24790     /**
24791      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24792      * {tag: "input", type: "checkbox", autocomplete: "off"})
24793      */
24794     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24795     /**
24796      * @cfg {String} boxLabel The text that appears beside the checkbox
24797      */
24798     boxLabel : "",
24799     /**
24800      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24801      */  
24802     inputValue : '1',
24803     /**
24804      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24805      */
24806      valueOff: '0', // value when not checked..
24807
24808     actionMode : 'viewEl', 
24809     //
24810     // private
24811     itemCls : 'x-menu-check-item x-form-item',
24812     groupClass : 'x-menu-group-item',
24813     inputType : 'hidden',
24814     
24815     
24816     inSetChecked: false, // check that we are not calling self...
24817     
24818     inputElement: false, // real input element?
24819     basedOn: false, // ????
24820     
24821     isFormField: true, // not sure where this is needed!!!!
24822
24823     onResize : function(){
24824         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24825         if(!this.boxLabel){
24826             this.el.alignTo(this.wrap, 'c-c');
24827         }
24828     },
24829
24830     initEvents : function(){
24831         Roo.form.Checkbox.superclass.initEvents.call(this);
24832         this.el.on("click", this.onClick,  this);
24833         this.el.on("change", this.onClick,  this);
24834     },
24835
24836
24837     getResizeEl : function(){
24838         return this.wrap;
24839     },
24840
24841     getPositionEl : function(){
24842         return this.wrap;
24843     },
24844
24845     // private
24846     onRender : function(ct, position){
24847         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24848         /*
24849         if(this.inputValue !== undefined){
24850             this.el.dom.value = this.inputValue;
24851         }
24852         */
24853         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24854         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24855         var viewEl = this.wrap.createChild({ 
24856             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24857         this.viewEl = viewEl;   
24858         this.wrap.on('click', this.onClick,  this); 
24859         
24860         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24861         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24862         
24863         
24864         
24865         if(this.boxLabel){
24866             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24867         //    viewEl.on('click', this.onClick,  this); 
24868         }
24869         //if(this.checked){
24870             this.setChecked(this.checked);
24871         //}else{
24872             //this.checked = this.el.dom;
24873         //}
24874
24875     },
24876
24877     // private
24878     initValue : Roo.emptyFn,
24879
24880     /**
24881      * Returns the checked state of the checkbox.
24882      * @return {Boolean} True if checked, else false
24883      */
24884     getValue : function(){
24885         if(this.el){
24886             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24887         }
24888         return this.valueOff;
24889         
24890     },
24891
24892         // private
24893     onClick : function(){ 
24894         this.setChecked(!this.checked);
24895
24896         //if(this.el.dom.checked != this.checked){
24897         //    this.setValue(this.el.dom.checked);
24898        // }
24899     },
24900
24901     /**
24902      * Sets the checked state of the checkbox.
24903      * On is always based on a string comparison between inputValue and the param.
24904      * @param {Boolean/String} value - the value to set 
24905      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24906      */
24907     setValue : function(v,suppressEvent){
24908         
24909         
24910         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24911         //if(this.el && this.el.dom){
24912         //    this.el.dom.checked = this.checked;
24913         //    this.el.dom.defaultChecked = this.checked;
24914         //}
24915         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24916         //this.fireEvent("check", this, this.checked);
24917     },
24918     // private..
24919     setChecked : function(state,suppressEvent)
24920     {
24921         if (this.inSetChecked) {
24922             this.checked = state;
24923             return;
24924         }
24925         
24926     
24927         if(this.wrap){
24928             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24929         }
24930         this.checked = state;
24931         if(suppressEvent !== true){
24932             this.fireEvent('check', this, state);
24933         }
24934         this.inSetChecked = true;
24935         this.el.dom.value = state ? this.inputValue : this.valueOff;
24936         this.inSetChecked = false;
24937         
24938     },
24939     // handle setting of hidden value by some other method!!?!?
24940     setFromHidden: function()
24941     {
24942         if(!this.el){
24943             return;
24944         }
24945         //console.log("SET FROM HIDDEN");
24946         //alert('setFrom hidden');
24947         this.setValue(this.el.dom.value);
24948     },
24949     
24950     onDestroy : function()
24951     {
24952         if(this.viewEl){
24953             Roo.get(this.viewEl).remove();
24954         }
24955          
24956         Roo.form.Checkbox.superclass.onDestroy.call(this);
24957     }
24958
24959 });/*
24960  * Based on:
24961  * Ext JS Library 1.1.1
24962  * Copyright(c) 2006-2007, Ext JS, LLC.
24963  *
24964  * Originally Released Under LGPL - original licence link has changed is not relivant.
24965  *
24966  * Fork - LGPL
24967  * <script type="text/javascript">
24968  */
24969  
24970 /**
24971  * @class Roo.form.Radio
24972  * @extends Roo.form.Checkbox
24973  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24974  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24975  * @constructor
24976  * Creates a new Radio
24977  * @param {Object} config Configuration options
24978  */
24979 Roo.form.Radio = function(){
24980     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24981 };
24982 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24983     inputType: 'radio',
24984
24985     /**
24986      * If this radio is part of a group, it will return the selected value
24987      * @return {String}
24988      */
24989     getGroupValue : function(){
24990         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24991     }
24992 });//<script type="text/javascript">
24993
24994 /*
24995  * Ext JS Library 1.1.1
24996  * Copyright(c) 2006-2007, Ext JS, LLC.
24997  * licensing@extjs.com
24998  * 
24999  * http://www.extjs.com/license
25000  */
25001  
25002  /*
25003   * 
25004   * Known bugs:
25005   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
25006   * - IE ? - no idea how much works there.
25007   * 
25008   * 
25009   * 
25010   */
25011  
25012
25013 /**
25014  * @class Ext.form.HtmlEditor
25015  * @extends Ext.form.Field
25016  * Provides a lightweight HTML Editor component.
25017  *
25018  * This has been tested on Fireforx / Chrome.. IE may not be so great..
25019  * 
25020  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
25021  * supported by this editor.</b><br/><br/>
25022  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
25023  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25024  */
25025 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
25026       /**
25027      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25028      */
25029     toolbars : false,
25030     /**
25031      * @cfg {String} createLinkText The default text for the create link prompt
25032      */
25033     createLinkText : 'Please enter the URL for the link:',
25034     /**
25035      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
25036      */
25037     defaultLinkValue : 'http:/'+'/',
25038    
25039      /**
25040      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25041      *                        Roo.resizable.
25042      */
25043     resizable : false,
25044      /**
25045      * @cfg {Number} height (in pixels)
25046      */   
25047     height: 300,
25048    /**
25049      * @cfg {Number} width (in pixels)
25050      */   
25051     width: 500,
25052     
25053     /**
25054      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25055      * 
25056      */
25057     stylesheets: false,
25058     
25059     // id of frame..
25060     frameId: false,
25061     
25062     // private properties
25063     validationEvent : false,
25064     deferHeight: true,
25065     initialized : false,
25066     activated : false,
25067     sourceEditMode : false,
25068     onFocus : Roo.emptyFn,
25069     iframePad:3,
25070     hideMode:'offsets',
25071     
25072     defaultAutoCreate : { // modified by initCompnoent..
25073         tag: "textarea",
25074         style:"width:500px;height:300px;",
25075         autocomplete: "off"
25076     },
25077
25078     // private
25079     initComponent : function(){
25080         this.addEvents({
25081             /**
25082              * @event initialize
25083              * Fires when the editor is fully initialized (including the iframe)
25084              * @param {HtmlEditor} this
25085              */
25086             initialize: true,
25087             /**
25088              * @event activate
25089              * Fires when the editor is first receives the focus. Any insertion must wait
25090              * until after this event.
25091              * @param {HtmlEditor} this
25092              */
25093             activate: true,
25094              /**
25095              * @event beforesync
25096              * Fires before the textarea is updated with content from the editor iframe. Return false
25097              * to cancel the sync.
25098              * @param {HtmlEditor} this
25099              * @param {String} html
25100              */
25101             beforesync: true,
25102              /**
25103              * @event beforepush
25104              * Fires before the iframe editor is updated with content from the textarea. Return false
25105              * to cancel the push.
25106              * @param {HtmlEditor} this
25107              * @param {String} html
25108              */
25109             beforepush: true,
25110              /**
25111              * @event sync
25112              * Fires when the textarea is updated with content from the editor iframe.
25113              * @param {HtmlEditor} this
25114              * @param {String} html
25115              */
25116             sync: true,
25117              /**
25118              * @event push
25119              * Fires when the iframe editor is updated with content from the textarea.
25120              * @param {HtmlEditor} this
25121              * @param {String} html
25122              */
25123             push: true,
25124              /**
25125              * @event editmodechange
25126              * Fires when the editor switches edit modes
25127              * @param {HtmlEditor} this
25128              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25129              */
25130             editmodechange: true,
25131             /**
25132              * @event editorevent
25133              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25134              * @param {HtmlEditor} this
25135              */
25136             editorevent: true
25137         });
25138         this.defaultAutoCreate =  {
25139             tag: "textarea",
25140             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
25141             autocomplete: "off"
25142         };
25143     },
25144
25145     /**
25146      * Protected method that will not generally be called directly. It
25147      * is called when the editor creates its toolbar. Override this method if you need to
25148      * add custom toolbar buttons.
25149      * @param {HtmlEditor} editor
25150      */
25151     createToolbar : function(editor){
25152         if (!editor.toolbars || !editor.toolbars.length) {
25153             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
25154         }
25155         
25156         for (var i =0 ; i < editor.toolbars.length;i++) {
25157             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
25158             editor.toolbars[i].init(editor);
25159         }
25160          
25161         
25162     },
25163
25164     /**
25165      * Protected method that will not generally be called directly. It
25166      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25167      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25168      */
25169     getDocMarkup : function(){
25170         // body styles..
25171         var st = '';
25172         if (this.stylesheets === false) {
25173             
25174             Roo.get(document.head).select('style').each(function(node) {
25175                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25176             });
25177             
25178             Roo.get(document.head).select('link').each(function(node) { 
25179                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25180             });
25181             
25182         } else if (!this.stylesheets.length) {
25183                 // simple..
25184                 st = '<style type="text/css">' +
25185                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25186                    '</style>';
25187         } else {
25188             Roo.each(this.stylesheets, function(s) {
25189                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
25190             });
25191             
25192         }
25193         
25194         st +=  '<style type="text/css">' +
25195             'IMG { cursor: pointer } ' +
25196         '</style>';
25197
25198         
25199         return '<html><head>' + st  +
25200             //<style type="text/css">' +
25201             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25202             //'</style>' +
25203             ' </head><body class="roo-htmleditor-body"></body></html>';
25204     },
25205
25206     // private
25207     onRender : function(ct, position)
25208     {
25209         var _t = this;
25210         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
25211         this.el.dom.style.border = '0 none';
25212         this.el.dom.setAttribute('tabIndex', -1);
25213         this.el.addClass('x-hidden');
25214         if(Roo.isIE){ // fix IE 1px bogus margin
25215             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25216         }
25217         this.wrap = this.el.wrap({
25218             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25219         });
25220         
25221         if (this.resizable) {
25222             this.resizeEl = new Roo.Resizable(this.wrap, {
25223                 pinned : true,
25224                 wrap: true,
25225                 dynamic : true,
25226                 minHeight : this.height,
25227                 height: this.height,
25228                 handles : this.resizable,
25229                 width: this.width,
25230                 listeners : {
25231                     resize : function(r, w, h) {
25232                         _t.onResize(w,h); // -something
25233                     }
25234                 }
25235             });
25236             
25237         }
25238
25239         this.frameId = Roo.id();
25240         
25241         this.createToolbar(this);
25242         
25243       
25244         
25245         var iframe = this.wrap.createChild({
25246             tag: 'iframe',
25247             id: this.frameId,
25248             name: this.frameId,
25249             frameBorder : 'no',
25250             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25251         }, this.el
25252         );
25253         
25254        // console.log(iframe);
25255         //this.wrap.dom.appendChild(iframe);
25256
25257         this.iframe = iframe.dom;
25258
25259          this.assignDocWin();
25260         
25261         this.doc.designMode = 'on';
25262        
25263         this.doc.open();
25264         this.doc.write(this.getDocMarkup());
25265         this.doc.close();
25266
25267         
25268         var task = { // must defer to wait for browser to be ready
25269             run : function(){
25270                 //console.log("run task?" + this.doc.readyState);
25271                 this.assignDocWin();
25272                 if(this.doc.body || this.doc.readyState == 'complete'){
25273                     try {
25274                         this.doc.designMode="on";
25275                     } catch (e) {
25276                         return;
25277                     }
25278                     Roo.TaskMgr.stop(task);
25279                     this.initEditor.defer(10, this);
25280                 }
25281             },
25282             interval : 10,
25283             duration:10000,
25284             scope: this
25285         };
25286         Roo.TaskMgr.start(task);
25287
25288         if(!this.width){
25289             this.setSize(this.wrap.getSize());
25290         }
25291         if (this.resizeEl) {
25292             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25293             // should trigger onReize..
25294         }
25295     },
25296
25297     // private
25298     onResize : function(w, h)
25299     {
25300         //Roo.log('resize: ' +w + ',' + h );
25301         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
25302         if(this.el && this.iframe){
25303             if(typeof w == 'number'){
25304                 var aw = w - this.wrap.getFrameWidth('lr');
25305                 this.el.setWidth(this.adjustWidth('textarea', aw));
25306                 this.iframe.style.width = aw + 'px';
25307             }
25308             if(typeof h == 'number'){
25309                 var tbh = 0;
25310                 for (var i =0; i < this.toolbars.length;i++) {
25311                     // fixme - ask toolbars for heights?
25312                     tbh += this.toolbars[i].tb.el.getHeight();
25313                     if (this.toolbars[i].footer) {
25314                         tbh += this.toolbars[i].footer.el.getHeight();
25315                     }
25316                 }
25317                 
25318                 
25319                 
25320                 
25321                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25322                 ah -= 5; // knock a few pixes off for look..
25323                 this.el.setHeight(this.adjustWidth('textarea', ah));
25324                 this.iframe.style.height = ah + 'px';
25325                 if(this.doc){
25326                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
25327                 }
25328             }
25329         }
25330     },
25331
25332     /**
25333      * Toggles the editor between standard and source edit mode.
25334      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25335      */
25336     toggleSourceEdit : function(sourceEditMode){
25337         
25338         this.sourceEditMode = sourceEditMode === true;
25339         
25340         if(this.sourceEditMode){
25341           
25342             this.syncValue();
25343             this.iframe.className = 'x-hidden';
25344             this.el.removeClass('x-hidden');
25345             this.el.dom.removeAttribute('tabIndex');
25346             this.el.focus();
25347         }else{
25348              
25349             this.pushValue();
25350             this.iframe.className = '';
25351             this.el.addClass('x-hidden');
25352             this.el.dom.setAttribute('tabIndex', -1);
25353             this.deferFocus();
25354         }
25355         this.setSize(this.wrap.getSize());
25356         this.fireEvent('editmodechange', this, this.sourceEditMode);
25357     },
25358
25359     // private used internally
25360     createLink : function(){
25361         var url = prompt(this.createLinkText, this.defaultLinkValue);
25362         if(url && url != 'http:/'+'/'){
25363             this.relayCmd('createlink', url);
25364         }
25365     },
25366
25367     // private (for BoxComponent)
25368     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25369
25370     // private (for BoxComponent)
25371     getResizeEl : function(){
25372         return this.wrap;
25373     },
25374
25375     // private (for BoxComponent)
25376     getPositionEl : function(){
25377         return this.wrap;
25378     },
25379
25380     // private
25381     initEvents : function(){
25382         this.originalValue = this.getValue();
25383     },
25384
25385     /**
25386      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25387      * @method
25388      */
25389     markInvalid : Roo.emptyFn,
25390     /**
25391      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25392      * @method
25393      */
25394     clearInvalid : Roo.emptyFn,
25395
25396     setValue : function(v){
25397         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25398         this.pushValue();
25399     },
25400
25401     /**
25402      * Protected method that will not generally be called directly. If you need/want
25403      * custom HTML cleanup, this is the method you should override.
25404      * @param {String} html The HTML to be cleaned
25405      * return {String} The cleaned HTML
25406      */
25407     cleanHtml : function(html){
25408         html = String(html);
25409         if(html.length > 5){
25410             if(Roo.isSafari){ // strip safari nonsense
25411                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25412             }
25413         }
25414         if(html == '&nbsp;'){
25415             html = '';
25416         }
25417         return html;
25418     },
25419
25420     /**
25421      * Protected method that will not generally be called directly. Syncs the contents
25422      * of the editor iframe with the textarea.
25423      */
25424     syncValue : function(){
25425         if(this.initialized){
25426             var bd = (this.doc.body || this.doc.documentElement);
25427             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25428             var html = bd.innerHTML;
25429             if(Roo.isSafari){
25430                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25431                 var m = bs.match(/text-align:(.*?);/i);
25432                 if(m && m[1]){
25433                     html = '<div style="'+m[0]+'">' + html + '</div>';
25434                 }
25435             }
25436             html = this.cleanHtml(html);
25437             // fix up the special chars..
25438             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25439                 return "&#"+b.charCodeAt()+";" 
25440             });
25441             if(this.fireEvent('beforesync', this, html) !== false){
25442                 this.el.dom.value = html;
25443                 this.fireEvent('sync', this, html);
25444             }
25445         }
25446     },
25447
25448     /**
25449      * Protected method that will not generally be called directly. Pushes the value of the textarea
25450      * into the iframe editor.
25451      */
25452     pushValue : function(){
25453         if(this.initialized){
25454             var v = this.el.dom.value;
25455             if(v.length < 1){
25456                 v = '&#160;';
25457             }
25458             
25459             if(this.fireEvent('beforepush', this, v) !== false){
25460                 var d = (this.doc.body || this.doc.documentElement);
25461                 d.innerHTML = v;
25462                 this.cleanUpPaste();
25463                 this.el.dom.value = d.innerHTML;
25464                 this.fireEvent('push', this, v);
25465             }
25466         }
25467     },
25468
25469     // private
25470     deferFocus : function(){
25471         this.focus.defer(10, this);
25472     },
25473
25474     // doc'ed in Field
25475     focus : function(){
25476         if(this.win && !this.sourceEditMode){
25477             this.win.focus();
25478         }else{
25479             this.el.focus();
25480         }
25481     },
25482     
25483     assignDocWin: function()
25484     {
25485         var iframe = this.iframe;
25486         
25487          if(Roo.isIE){
25488             this.doc = iframe.contentWindow.document;
25489             this.win = iframe.contentWindow;
25490         } else {
25491             if (!Roo.get(this.frameId)) {
25492                 return;
25493             }
25494             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25495             this.win = Roo.get(this.frameId).dom.contentWindow;
25496         }
25497     },
25498     
25499     // private
25500     initEditor : function(){
25501         //console.log("INIT EDITOR");
25502         this.assignDocWin();
25503         
25504         
25505         
25506         this.doc.designMode="on";
25507         this.doc.open();
25508         this.doc.write(this.getDocMarkup());
25509         this.doc.close();
25510         
25511         var dbody = (this.doc.body || this.doc.documentElement);
25512         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25513         // this copies styles from the containing element into thsi one..
25514         // not sure why we need all of this..
25515         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25516         ss['background-attachment'] = 'fixed'; // w3c
25517         dbody.bgProperties = 'fixed'; // ie
25518         Roo.DomHelper.applyStyles(dbody, ss);
25519         Roo.EventManager.on(this.doc, {
25520             //'mousedown': this.onEditorEvent,
25521             'mouseup': this.onEditorEvent,
25522             'dblclick': this.onEditorEvent,
25523             'click': this.onEditorEvent,
25524             'keyup': this.onEditorEvent,
25525             buffer:100,
25526             scope: this
25527         });
25528         if(Roo.isGecko){
25529             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25530         }
25531         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25532             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25533         }
25534         this.initialized = true;
25535
25536         this.fireEvent('initialize', this);
25537         this.pushValue();
25538     },
25539
25540     // private
25541     onDestroy : function(){
25542         
25543         
25544         
25545         if(this.rendered){
25546             
25547             for (var i =0; i < this.toolbars.length;i++) {
25548                 // fixme - ask toolbars for heights?
25549                 this.toolbars[i].onDestroy();
25550             }
25551             
25552             this.wrap.dom.innerHTML = '';
25553             this.wrap.remove();
25554         }
25555     },
25556
25557     // private
25558     onFirstFocus : function(){
25559         
25560         this.assignDocWin();
25561         
25562         
25563         this.activated = true;
25564         for (var i =0; i < this.toolbars.length;i++) {
25565             this.toolbars[i].onFirstFocus();
25566         }
25567        
25568         if(Roo.isGecko){ // prevent silly gecko errors
25569             this.win.focus();
25570             var s = this.win.getSelection();
25571             if(!s.focusNode || s.focusNode.nodeType != 3){
25572                 var r = s.getRangeAt(0);
25573                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25574                 r.collapse(true);
25575                 this.deferFocus();
25576             }
25577             try{
25578                 this.execCmd('useCSS', true);
25579                 this.execCmd('styleWithCSS', false);
25580             }catch(e){}
25581         }
25582         this.fireEvent('activate', this);
25583     },
25584
25585     // private
25586     adjustFont: function(btn){
25587         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25588         //if(Roo.isSafari){ // safari
25589         //    adjust *= 2;
25590        // }
25591         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25592         if(Roo.isSafari){ // safari
25593             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25594             v =  (v < 10) ? 10 : v;
25595             v =  (v > 48) ? 48 : v;
25596             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25597             
25598         }
25599         
25600         
25601         v = Math.max(1, v+adjust);
25602         
25603         this.execCmd('FontSize', v  );
25604     },
25605
25606     onEditorEvent : function(e){
25607         this.fireEvent('editorevent', this, e);
25608       //  this.updateToolbar();
25609         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25610     },
25611
25612     insertTag : function(tg)
25613     {
25614         // could be a bit smarter... -> wrap the current selected tRoo..
25615         
25616         this.execCmd("formatblock",   tg);
25617         
25618     },
25619     
25620     insertText : function(txt)
25621     {
25622         
25623         
25624         range = this.createRange();
25625         range.deleteContents();
25626                //alert(Sender.getAttribute('label'));
25627                
25628         range.insertNode(this.doc.createTextNode(txt));
25629     } ,
25630     
25631     // private
25632     relayBtnCmd : function(btn){
25633         this.relayCmd(btn.cmd);
25634     },
25635
25636     /**
25637      * Executes a Midas editor command on the editor document and performs necessary focus and
25638      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25639      * @param {String} cmd The Midas command
25640      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25641      */
25642     relayCmd : function(cmd, value){
25643         this.win.focus();
25644         this.execCmd(cmd, value);
25645         this.fireEvent('editorevent', this);
25646         //this.updateToolbar();
25647         this.deferFocus();
25648     },
25649
25650     /**
25651      * Executes a Midas editor command directly on the editor document.
25652      * For visual commands, you should use {@link #relayCmd} instead.
25653      * <b>This should only be called after the editor is initialized.</b>
25654      * @param {String} cmd The Midas command
25655      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25656      */
25657     execCmd : function(cmd, value){
25658         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25659         this.syncValue();
25660     },
25661  
25662  
25663    
25664     /**
25665      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25666      * to insert tRoo.
25667      * @param {String} text | dom node.. 
25668      */
25669     insertAtCursor : function(text)
25670     {
25671         
25672         
25673         
25674         if(!this.activated){
25675             return;
25676         }
25677         /*
25678         if(Roo.isIE){
25679             this.win.focus();
25680             var r = this.doc.selection.createRange();
25681             if(r){
25682                 r.collapse(true);
25683                 r.pasteHTML(text);
25684                 this.syncValue();
25685                 this.deferFocus();
25686             
25687             }
25688             return;
25689         }
25690         */
25691         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25692             this.win.focus();
25693             
25694             
25695             // from jquery ui (MIT licenced)
25696             var range, node;
25697             var win = this.win;
25698             
25699             if (win.getSelection && win.getSelection().getRangeAt) {
25700                 range = win.getSelection().getRangeAt(0);
25701                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25702                 range.insertNode(node);
25703             } else if (win.document.selection && win.document.selection.createRange) {
25704                 // no firefox support
25705                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25706                 win.document.selection.createRange().pasteHTML(txt);
25707             } else {
25708                 // no firefox support
25709                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25710                 this.execCmd('InsertHTML', txt);
25711             } 
25712             
25713             this.syncValue();
25714             
25715             this.deferFocus();
25716         }
25717     },
25718  // private
25719     mozKeyPress : function(e){
25720         if(e.ctrlKey){
25721             var c = e.getCharCode(), cmd;
25722           
25723             if(c > 0){
25724                 c = String.fromCharCode(c).toLowerCase();
25725                 switch(c){
25726                     case 'b':
25727                         cmd = 'bold';
25728                         break;
25729                     case 'i':
25730                         cmd = 'italic';
25731                         break;
25732                     
25733                     case 'u':
25734                         cmd = 'underline';
25735                         break;
25736                     
25737                     case 'v':
25738                         this.cleanUpPaste.defer(100, this);
25739                         return;
25740                         
25741                 }
25742                 if(cmd){
25743                     this.win.focus();
25744                     this.execCmd(cmd);
25745                     this.deferFocus();
25746                     e.preventDefault();
25747                 }
25748                 
25749             }
25750         }
25751     },
25752
25753     // private
25754     fixKeys : function(){ // load time branching for fastest keydown performance
25755         if(Roo.isIE){
25756             return function(e){
25757                 var k = e.getKey(), r;
25758                 if(k == e.TAB){
25759                     e.stopEvent();
25760                     r = this.doc.selection.createRange();
25761                     if(r){
25762                         r.collapse(true);
25763                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25764                         this.deferFocus();
25765                     }
25766                     return;
25767                 }
25768                 
25769                 if(k == e.ENTER){
25770                     r = this.doc.selection.createRange();
25771                     if(r){
25772                         var target = r.parentElement();
25773                         if(!target || target.tagName.toLowerCase() != 'li'){
25774                             e.stopEvent();
25775                             r.pasteHTML('<br />');
25776                             r.collapse(false);
25777                             r.select();
25778                         }
25779                     }
25780                 }
25781                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25782                     this.cleanUpPaste.defer(100, this);
25783                     return;
25784                 }
25785                 
25786                 
25787             };
25788         }else if(Roo.isOpera){
25789             return function(e){
25790                 var k = e.getKey();
25791                 if(k == e.TAB){
25792                     e.stopEvent();
25793                     this.win.focus();
25794                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25795                     this.deferFocus();
25796                 }
25797                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25798                     this.cleanUpPaste.defer(100, this);
25799                     return;
25800                 }
25801                 
25802             };
25803         }else if(Roo.isSafari){
25804             return function(e){
25805                 var k = e.getKey();
25806                 
25807                 if(k == e.TAB){
25808                     e.stopEvent();
25809                     this.execCmd('InsertText','\t');
25810                     this.deferFocus();
25811                     return;
25812                 }
25813                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25814                     this.cleanUpPaste.defer(100, this);
25815                     return;
25816                 }
25817                 
25818              };
25819         }
25820     }(),
25821     
25822     getAllAncestors: function()
25823     {
25824         var p = this.getSelectedNode();
25825         var a = [];
25826         if (!p) {
25827             a.push(p); // push blank onto stack..
25828             p = this.getParentElement();
25829         }
25830         
25831         
25832         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25833             a.push(p);
25834             p = p.parentNode;
25835         }
25836         a.push(this.doc.body);
25837         return a;
25838     },
25839     lastSel : false,
25840     lastSelNode : false,
25841     
25842     
25843     getSelection : function() 
25844     {
25845         this.assignDocWin();
25846         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25847     },
25848     
25849     getSelectedNode: function() 
25850     {
25851         // this may only work on Gecko!!!
25852         
25853         // should we cache this!!!!
25854         
25855         
25856         
25857          
25858         var range = this.createRange(this.getSelection()).cloneRange();
25859         
25860         if (Roo.isIE) {
25861             var parent = range.parentElement();
25862             while (true) {
25863                 var testRange = range.duplicate();
25864                 testRange.moveToElementText(parent);
25865                 if (testRange.inRange(range)) {
25866                     break;
25867                 }
25868                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25869                     break;
25870                 }
25871                 parent = parent.parentElement;
25872             }
25873             return parent;
25874         }
25875         
25876         // is ancestor a text element.
25877         var ac =  range.commonAncestorContainer;
25878         if (ac.nodeType == 3) {
25879             ac = ac.parentNode;
25880         }
25881         
25882         var ar = ac.childNodes;
25883          
25884         var nodes = [];
25885         var other_nodes = [];
25886         var has_other_nodes = false;
25887         for (var i=0;i<ar.length;i++) {
25888             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25889                 continue;
25890             }
25891             // fullly contained node.
25892             
25893             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25894                 nodes.push(ar[i]);
25895                 continue;
25896             }
25897             
25898             // probably selected..
25899             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25900                 other_nodes.push(ar[i]);
25901                 continue;
25902             }
25903             // outer..
25904             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25905                 continue;
25906             }
25907             
25908             
25909             has_other_nodes = true;
25910         }
25911         if (!nodes.length && other_nodes.length) {
25912             nodes= other_nodes;
25913         }
25914         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25915             return false;
25916         }
25917         
25918         return nodes[0];
25919     },
25920     createRange: function(sel)
25921     {
25922         // this has strange effects when using with 
25923         // top toolbar - not sure if it's a great idea.
25924         //this.editor.contentWindow.focus();
25925         if (typeof sel != "undefined") {
25926             try {
25927                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25928             } catch(e) {
25929                 return this.doc.createRange();
25930             }
25931         } else {
25932             return this.doc.createRange();
25933         }
25934     },
25935     getParentElement: function()
25936     {
25937         
25938         this.assignDocWin();
25939         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25940         
25941         var range = this.createRange(sel);
25942          
25943         try {
25944             var p = range.commonAncestorContainer;
25945             while (p.nodeType == 3) { // text node
25946                 p = p.parentNode;
25947             }
25948             return p;
25949         } catch (e) {
25950             return null;
25951         }
25952     
25953     },
25954     /***
25955      *
25956      * Range intersection.. the hard stuff...
25957      *  '-1' = before
25958      *  '0' = hits..
25959      *  '1' = after.
25960      *         [ -- selected range --- ]
25961      *   [fail]                        [fail]
25962      *
25963      *    basically..
25964      *      if end is before start or  hits it. fail.
25965      *      if start is after end or hits it fail.
25966      *
25967      *   if either hits (but other is outside. - then it's not 
25968      *   
25969      *    
25970      **/
25971     
25972     
25973     // @see http://www.thismuchiknow.co.uk/?p=64.
25974     rangeIntersectsNode : function(range, node)
25975     {
25976         var nodeRange = node.ownerDocument.createRange();
25977         try {
25978             nodeRange.selectNode(node);
25979         } catch (e) {
25980             nodeRange.selectNodeContents(node);
25981         }
25982     
25983         var rangeStartRange = range.cloneRange();
25984         rangeStartRange.collapse(true);
25985     
25986         var rangeEndRange = range.cloneRange();
25987         rangeEndRange.collapse(false);
25988     
25989         var nodeStartRange = nodeRange.cloneRange();
25990         nodeStartRange.collapse(true);
25991     
25992         var nodeEndRange = nodeRange.cloneRange();
25993         nodeEndRange.collapse(false);
25994     
25995         return rangeStartRange.compareBoundaryPoints(
25996                  Range.START_TO_START, nodeEndRange) == -1 &&
25997                rangeEndRange.compareBoundaryPoints(
25998                  Range.START_TO_START, nodeStartRange) == 1;
25999         
26000          
26001     },
26002     rangeCompareNode : function(range, node)
26003     {
26004         var nodeRange = node.ownerDocument.createRange();
26005         try {
26006             nodeRange.selectNode(node);
26007         } catch (e) {
26008             nodeRange.selectNodeContents(node);
26009         }
26010         
26011         
26012         range.collapse(true);
26013     
26014         nodeRange.collapse(true);
26015      
26016         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26017         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26018          
26019         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26020         
26021         var nodeIsBefore   =  ss == 1;
26022         var nodeIsAfter    = ee == -1;
26023         
26024         if (nodeIsBefore && nodeIsAfter)
26025             return 0; // outer
26026         if (!nodeIsBefore && nodeIsAfter)
26027             return 1; //right trailed.
26028         
26029         if (nodeIsBefore && !nodeIsAfter)
26030             return 2;  // left trailed.
26031         // fully contined.
26032         return 3;
26033     },
26034
26035     // private? - in a new class?
26036     cleanUpPaste :  function()
26037     {
26038         // cleans up the whole document..
26039          Roo.log('cleanuppaste');
26040         this.cleanUpChildren(this.doc.body);
26041         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26042         if (clean != this.doc.body.innerHTML) {
26043             this.doc.body.innerHTML = clean;
26044         }
26045         
26046     },
26047     
26048     cleanWordChars : function(input) {
26049         var he = Roo.form.HtmlEditor;
26050     
26051         var output = input;
26052         Roo.each(he.swapCodes, function(sw) { 
26053         
26054             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26055             output = output.replace(swapper, sw[1]);
26056         });
26057         return output;
26058     },
26059     
26060     
26061     cleanUpChildren : function (n)
26062     {
26063         if (!n.childNodes.length) {
26064             return;
26065         }
26066         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26067            this.cleanUpChild(n.childNodes[i]);
26068         }
26069     },
26070     
26071     
26072         
26073     
26074     cleanUpChild : function (node)
26075     {
26076         //console.log(node);
26077         if (node.nodeName == "#text") {
26078             // clean up silly Windows -- stuff?
26079             return; 
26080         }
26081         if (node.nodeName == "#comment") {
26082             node.parentNode.removeChild(node);
26083             // clean up silly Windows -- stuff?
26084             return; 
26085         }
26086         
26087         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
26088             // remove node.
26089             node.parentNode.removeChild(node);
26090             return;
26091             
26092         }
26093         
26094         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
26095         
26096         // remove <a name=....> as rendering on yahoo mailer is bored with this.
26097         
26098         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26099             remove_keep_children = true;
26100         }
26101         
26102         if (remove_keep_children) {
26103             this.cleanUpChildren(node);
26104             // inserts everything just before this node...
26105             while (node.childNodes.length) {
26106                 var cn = node.childNodes[0];
26107                 node.removeChild(cn);
26108                 node.parentNode.insertBefore(cn, node);
26109             }
26110             node.parentNode.removeChild(node);
26111             return;
26112         }
26113         
26114         if (!node.attributes || !node.attributes.length) {
26115             this.cleanUpChildren(node);
26116             return;
26117         }
26118         
26119         function cleanAttr(n,v)
26120         {
26121             
26122             if (v.match(/^\./) || v.match(/^\//)) {
26123                 return;
26124             }
26125             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
26126                 return;
26127             }
26128             if (v.match(/^#/)) {
26129                 return;
26130             }
26131             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
26132             node.removeAttribute(n);
26133             
26134         }
26135         
26136         function cleanStyle(n,v)
26137         {
26138             if (v.match(/expression/)) { //XSS?? should we even bother..
26139                 node.removeAttribute(n);
26140                 return;
26141             }
26142             
26143             
26144             var parts = v.split(/;/);
26145             Roo.each(parts, function(p) {
26146                 p = p.replace(/\s+/g,'');
26147                 if (!p.length) {
26148                     return true;
26149                 }
26150                 var l = p.split(':').shift().replace(/\s+/g,'');
26151                 
26152                 // only allow 'c whitelisted system attributes'
26153                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
26154                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
26155                     node.removeAttribute(n);
26156                     return false;
26157                 }
26158                 return true;
26159             });
26160             
26161             
26162         }
26163         
26164         
26165         for (var i = node.attributes.length-1; i > -1 ; i--) {
26166             var a = node.attributes[i];
26167             //console.log(a);
26168             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
26169                 node.removeAttribute(a.name);
26170                 continue;
26171             }
26172             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
26173                 cleanAttr(a.name,a.value); // fixme..
26174                 continue;
26175             }
26176             if (a.name == 'style') {
26177                 cleanStyle(a.name,a.value);
26178                 continue;
26179             }
26180             /// clean up MS crap..
26181             // tecnically this should be a list of valid class'es..
26182             
26183             
26184             if (a.name == 'class') {
26185                 if (a.value.match(/^Mso/)) {
26186                     node.className = '';
26187                 }
26188                 
26189                 if (a.value.match(/body/)) {
26190                     node.className = '';
26191                 }
26192                 continue;
26193             }
26194             
26195             // style cleanup!?
26196             // class cleanup?
26197             
26198         }
26199         
26200         
26201         this.cleanUpChildren(node);
26202         
26203         
26204     }
26205     
26206     
26207     // hide stuff that is not compatible
26208     /**
26209      * @event blur
26210      * @hide
26211      */
26212     /**
26213      * @event change
26214      * @hide
26215      */
26216     /**
26217      * @event focus
26218      * @hide
26219      */
26220     /**
26221      * @event specialkey
26222      * @hide
26223      */
26224     /**
26225      * @cfg {String} fieldClass @hide
26226      */
26227     /**
26228      * @cfg {String} focusClass @hide
26229      */
26230     /**
26231      * @cfg {String} autoCreate @hide
26232      */
26233     /**
26234      * @cfg {String} inputType @hide
26235      */
26236     /**
26237      * @cfg {String} invalidClass @hide
26238      */
26239     /**
26240      * @cfg {String} invalidText @hide
26241      */
26242     /**
26243      * @cfg {String} msgFx @hide
26244      */
26245     /**
26246      * @cfg {String} validateOnBlur @hide
26247      */
26248 });
26249
26250 Roo.form.HtmlEditor.white = [
26251         'area', 'br', 'img', 'input', 'hr', 'wbr',
26252         
26253        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26254        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26255        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26256        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26257        'table',   'ul',         'xmp', 
26258        
26259        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26260       'thead',   'tr', 
26261      
26262       'dir', 'menu', 'ol', 'ul', 'dl',
26263        
26264       'embed',  'object'
26265 ];
26266
26267
26268 Roo.form.HtmlEditor.black = [
26269     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26270         'applet', // 
26271         'base',   'basefont', 'bgsound', 'blink',  'body', 
26272         'frame',  'frameset', 'head',    'html',   'ilayer', 
26273         'iframe', 'layer',  'link',     'meta',    'object',   
26274         'script', 'style' ,'title',  'xml' // clean later..
26275 ];
26276 Roo.form.HtmlEditor.clean = [
26277     'script', 'style', 'title', 'xml'
26278 ];
26279 Roo.form.HtmlEditor.remove = [
26280     'font'
26281 ];
26282 // attributes..
26283
26284 Roo.form.HtmlEditor.ablack = [
26285     'on'
26286 ];
26287     
26288 Roo.form.HtmlEditor.aclean = [ 
26289     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26290 ];
26291
26292 // protocols..
26293 Roo.form.HtmlEditor.pwhite= [
26294         'http',  'https',  'mailto'
26295 ];
26296
26297 // white listed style attributes.
26298 Roo.form.HtmlEditor.cwhite= [
26299         'text-align',
26300         'font-size'
26301 ];
26302
26303
26304 Roo.form.HtmlEditor.swapCodes   =[ 
26305     [    8211, "--" ], 
26306     [    8212, "--" ], 
26307     [    8216,  "'" ],  
26308     [    8217, "'" ],  
26309     [    8220, '"' ],  
26310     [    8221, '"' ],  
26311     [    8226, "*" ],  
26312     [    8230, "..." ]
26313 ]; 
26314
26315     // <script type="text/javascript">
26316 /*
26317  * Based on
26318  * Ext JS Library 1.1.1
26319  * Copyright(c) 2006-2007, Ext JS, LLC.
26320  *  
26321  
26322  */
26323
26324 /**
26325  * @class Roo.form.HtmlEditorToolbar1
26326  * Basic Toolbar
26327  * 
26328  * Usage:
26329  *
26330  new Roo.form.HtmlEditor({
26331     ....
26332     toolbars : [
26333         new Roo.form.HtmlEditorToolbar1({
26334             disable : { fonts: 1 , format: 1, ..., ... , ...],
26335             btns : [ .... ]
26336         })
26337     }
26338      
26339  * 
26340  * @cfg {Object} disable List of elements to disable..
26341  * @cfg {Array} btns List of additional buttons.
26342  * 
26343  * 
26344  * NEEDS Extra CSS? 
26345  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26346  */
26347  
26348 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26349 {
26350     
26351     Roo.apply(this, config);
26352     
26353     // default disabled, based on 'good practice'..
26354     this.disable = this.disable || {};
26355     Roo.applyIf(this.disable, {
26356         fontSize : true,
26357         colors : true,
26358         specialElements : true
26359     });
26360     
26361     
26362     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26363     // dont call parent... till later.
26364 }
26365
26366 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26367     
26368     tb: false,
26369     
26370     rendered: false,
26371     
26372     editor : false,
26373     /**
26374      * @cfg {Object} disable  List of toolbar elements to disable
26375          
26376      */
26377     disable : false,
26378       /**
26379      * @cfg {Array} fontFamilies An array of available font families
26380      */
26381     fontFamilies : [
26382         'Arial',
26383         'Courier New',
26384         'Tahoma',
26385         'Times New Roman',
26386         'Verdana'
26387     ],
26388     
26389     specialChars : [
26390            "&#169;",
26391           "&#174;",     
26392           "&#8482;",    
26393           "&#163;" ,    
26394          // "&#8212;",    
26395           "&#8230;",    
26396           "&#247;" ,    
26397         //  "&#225;" ,     ?? a acute?
26398            "&#8364;"    , //Euro
26399        //   "&#8220;"    ,
26400         //  "&#8221;"    ,
26401         //  "&#8226;"    ,
26402           "&#176;"  //   , // degrees
26403
26404          // "&#233;"     , // e ecute
26405          // "&#250;"     , // u ecute?
26406     ],
26407     
26408     specialElements : [
26409         {
26410             text: "Insert Table",
26411             xtype: 'MenuItem',
26412             xns : Roo.Menu,
26413             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26414                 
26415         },
26416         {    
26417             text: "Insert Image",
26418             xtype: 'MenuItem',
26419             xns : Roo.Menu,
26420             ihtml : '<img src="about:blank"/>'
26421             
26422         }
26423         
26424          
26425     ],
26426     
26427     
26428     inputElements : [ 
26429             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26430             "input:submit", "input:button", "select", "textarea", "label" ],
26431     formats : [
26432         ["p"] ,  
26433         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26434         ["pre"],[ "code"], 
26435         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
26436     ],
26437      /**
26438      * @cfg {String} defaultFont default font to use.
26439      */
26440     defaultFont: 'tahoma',
26441    
26442     fontSelect : false,
26443     
26444     
26445     formatCombo : false,
26446     
26447     init : function(editor)
26448     {
26449         this.editor = editor;
26450         
26451         
26452         var fid = editor.frameId;
26453         var etb = this;
26454         function btn(id, toggle, handler){
26455             var xid = fid + '-'+ id ;
26456             return {
26457                 id : xid,
26458                 cmd : id,
26459                 cls : 'x-btn-icon x-edit-'+id,
26460                 enableToggle:toggle !== false,
26461                 scope: editor, // was editor...
26462                 handler:handler||editor.relayBtnCmd,
26463                 clickEvent:'mousedown',
26464                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26465                 tabIndex:-1
26466             };
26467         }
26468         
26469         
26470         
26471         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26472         this.tb = tb;
26473          // stop form submits
26474         tb.el.on('click', function(e){
26475             e.preventDefault(); // what does this do?
26476         });
26477
26478         if(!this.disable.font && !Roo.isSafari){
26479             /* why no safari for fonts
26480             editor.fontSelect = tb.el.createChild({
26481                 tag:'select',
26482                 tabIndex: -1,
26483                 cls:'x-font-select',
26484                 html: editor.createFontOptions()
26485             });
26486             editor.fontSelect.on('change', function(){
26487                 var font = editor.fontSelect.dom.value;
26488                 editor.relayCmd('fontname', font);
26489                 editor.deferFocus();
26490             }, editor);
26491             tb.add(
26492                 editor.fontSelect.dom,
26493                 '-'
26494             );
26495             */
26496         };
26497         if(!this.disable.formats){
26498             this.formatCombo = new Roo.form.ComboBox({
26499                 store: new Roo.data.SimpleStore({
26500                     id : 'tag',
26501                     fields: ['tag'],
26502                     data : this.formats // from states.js
26503                 }),
26504                 blockFocus : true,
26505                 //autoCreate : {tag: "div",  size: "20"},
26506                 displayField:'tag',
26507                 typeAhead: false,
26508                 mode: 'local',
26509                 editable : false,
26510                 triggerAction: 'all',
26511                 emptyText:'Add tag',
26512                 selectOnFocus:true,
26513                 width:135,
26514                 listeners : {
26515                     'select': function(c, r, i) {
26516                         editor.insertTag(r.get('tag'));
26517                         editor.focus();
26518                     }
26519                 }
26520
26521             });
26522             tb.addField(this.formatCombo);
26523             
26524         }
26525         
26526         if(!this.disable.format){
26527             tb.add(
26528                 btn('bold'),
26529                 btn('italic'),
26530                 btn('underline')
26531             );
26532         };
26533         if(!this.disable.fontSize){
26534             tb.add(
26535                 '-',
26536                 
26537                 
26538                 btn('increasefontsize', false, editor.adjustFont),
26539                 btn('decreasefontsize', false, editor.adjustFont)
26540             );
26541         };
26542         
26543         
26544         if(!this.disable.colors){
26545             tb.add(
26546                 '-', {
26547                     id:editor.frameId +'-forecolor',
26548                     cls:'x-btn-icon x-edit-forecolor',
26549                     clickEvent:'mousedown',
26550                     tooltip: this.buttonTips['forecolor'] || undefined,
26551                     tabIndex:-1,
26552                     menu : new Roo.menu.ColorMenu({
26553                         allowReselect: true,
26554                         focus: Roo.emptyFn,
26555                         value:'000000',
26556                         plain:true,
26557                         selectHandler: function(cp, color){
26558                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26559                             editor.deferFocus();
26560                         },
26561                         scope: editor,
26562                         clickEvent:'mousedown'
26563                     })
26564                 }, {
26565                     id:editor.frameId +'backcolor',
26566                     cls:'x-btn-icon x-edit-backcolor',
26567                     clickEvent:'mousedown',
26568                     tooltip: this.buttonTips['backcolor'] || undefined,
26569                     tabIndex:-1,
26570                     menu : new Roo.menu.ColorMenu({
26571                         focus: Roo.emptyFn,
26572                         value:'FFFFFF',
26573                         plain:true,
26574                         allowReselect: true,
26575                         selectHandler: function(cp, color){
26576                             if(Roo.isGecko){
26577                                 editor.execCmd('useCSS', false);
26578                                 editor.execCmd('hilitecolor', color);
26579                                 editor.execCmd('useCSS', true);
26580                                 editor.deferFocus();
26581                             }else{
26582                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26583                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26584                                 editor.deferFocus();
26585                             }
26586                         },
26587                         scope:editor,
26588                         clickEvent:'mousedown'
26589                     })
26590                 }
26591             );
26592         };
26593         // now add all the items...
26594         
26595
26596         if(!this.disable.alignments){
26597             tb.add(
26598                 '-',
26599                 btn('justifyleft'),
26600                 btn('justifycenter'),
26601                 btn('justifyright')
26602             );
26603         };
26604
26605         //if(!Roo.isSafari){
26606             if(!this.disable.links){
26607                 tb.add(
26608                     '-',
26609                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
26610                 );
26611             };
26612
26613             if(!this.disable.lists){
26614                 tb.add(
26615                     '-',
26616                     btn('insertorderedlist'),
26617                     btn('insertunorderedlist')
26618                 );
26619             }
26620             if(!this.disable.sourceEdit){
26621                 tb.add(
26622                     '-',
26623                     btn('sourceedit', true, function(btn){
26624                         this.toggleSourceEdit(btn.pressed);
26625                     })
26626                 );
26627             }
26628         //}
26629         
26630         var smenu = { };
26631         // special menu.. - needs to be tidied up..
26632         if (!this.disable.special) {
26633             smenu = {
26634                 text: "&#169;",
26635                 cls: 'x-edit-none',
26636                 
26637                 menu : {
26638                     items : []
26639                 }
26640             };
26641             for (var i =0; i < this.specialChars.length; i++) {
26642                 smenu.menu.items.push({
26643                     
26644                     html: this.specialChars[i],
26645                     handler: function(a,b) {
26646                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26647                         //editor.insertAtCursor(a.html);
26648                         
26649                     },
26650                     tabIndex:-1
26651                 });
26652             }
26653             
26654             
26655             tb.add(smenu);
26656             
26657             
26658         }
26659          
26660         if (!this.disable.specialElements) {
26661             var semenu = {
26662                 text: "Other;",
26663                 cls: 'x-edit-none',
26664                 menu : {
26665                     items : []
26666                 }
26667             };
26668             for (var i =0; i < this.specialElements.length; i++) {
26669                 semenu.menu.items.push(
26670                     Roo.apply({ 
26671                         handler: function(a,b) {
26672                             editor.insertAtCursor(this.ihtml);
26673                         }
26674                     }, this.specialElements[i])
26675                 );
26676                     
26677             }
26678             
26679             tb.add(semenu);
26680             
26681             
26682         }
26683          
26684         
26685         if (this.btns) {
26686             for(var i =0; i< this.btns.length;i++) {
26687                 var b = Roo.factory(this.btns[i],Roo.form);
26688                 b.cls =  'x-edit-none';
26689                 b.scope = editor;
26690                 tb.add(b);
26691             }
26692         
26693         }
26694         
26695         
26696         
26697         // disable everything...
26698         
26699         this.tb.items.each(function(item){
26700            if(item.id != editor.frameId+ '-sourceedit'){
26701                 item.disable();
26702             }
26703         });
26704         this.rendered = true;
26705         
26706         // the all the btns;
26707         editor.on('editorevent', this.updateToolbar, this);
26708         // other toolbars need to implement this..
26709         //editor.on('editmodechange', this.updateToolbar, this);
26710     },
26711     
26712     
26713     
26714     /**
26715      * Protected method that will not generally be called directly. It triggers
26716      * a toolbar update by reading the markup state of the current selection in the editor.
26717      */
26718     updateToolbar: function(){
26719
26720         if(!this.editor.activated){
26721             this.editor.onFirstFocus();
26722             return;
26723         }
26724
26725         var btns = this.tb.items.map, 
26726             doc = this.editor.doc,
26727             frameId = this.editor.frameId;
26728
26729         if(!this.disable.font && !Roo.isSafari){
26730             /*
26731             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
26732             if(name != this.fontSelect.dom.value){
26733                 this.fontSelect.dom.value = name;
26734             }
26735             */
26736         }
26737         if(!this.disable.format){
26738             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
26739             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
26740             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
26741         }
26742         if(!this.disable.alignments){
26743             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
26744             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
26745             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
26746         }
26747         if(!Roo.isSafari && !this.disable.lists){
26748             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
26749             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
26750         }
26751         
26752         var ans = this.editor.getAllAncestors();
26753         if (this.formatCombo) {
26754             
26755             
26756             var store = this.formatCombo.store;
26757             this.formatCombo.setValue("");
26758             for (var i =0; i < ans.length;i++) {
26759                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26760                     // select it..
26761                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26762                     break;
26763                 }
26764             }
26765         }
26766         
26767         
26768         
26769         // hides menus... - so this cant be on a menu...
26770         Roo.menu.MenuMgr.hideAll();
26771
26772         //this.editorsyncValue();
26773     },
26774    
26775     
26776     createFontOptions : function(){
26777         var buf = [], fs = this.fontFamilies, ff, lc;
26778         for(var i = 0, len = fs.length; i< len; i++){
26779             ff = fs[i];
26780             lc = ff.toLowerCase();
26781             buf.push(
26782                 '<option value="',lc,'" style="font-family:',ff,';"',
26783                     (this.defaultFont == lc ? ' selected="true">' : '>'),
26784                     ff,
26785                 '</option>'
26786             );
26787         }
26788         return buf.join('');
26789     },
26790     
26791     toggleSourceEdit : function(sourceEditMode){
26792         if(sourceEditMode === undefined){
26793             sourceEditMode = !this.sourceEditMode;
26794         }
26795         this.sourceEditMode = sourceEditMode === true;
26796         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
26797         // just toggle the button?
26798         if(btn.pressed !== this.editor.sourceEditMode){
26799             btn.toggle(this.editor.sourceEditMode);
26800             return;
26801         }
26802         
26803         if(this.sourceEditMode){
26804             this.tb.items.each(function(item){
26805                 if(item.cmd != 'sourceedit'){
26806                     item.disable();
26807                 }
26808             });
26809           
26810         }else{
26811             if(this.initialized){
26812                 this.tb.items.each(function(item){
26813                     item.enable();
26814                 });
26815             }
26816             
26817         }
26818         // tell the editor that it's been pressed..
26819         this.editor.toggleSourceEdit(sourceEditMode);
26820        
26821     },
26822      /**
26823      * Object collection of toolbar tooltips for the buttons in the editor. The key
26824      * is the command id associated with that button and the value is a valid QuickTips object.
26825      * For example:
26826 <pre><code>
26827 {
26828     bold : {
26829         title: 'Bold (Ctrl+B)',
26830         text: 'Make the selected text bold.',
26831         cls: 'x-html-editor-tip'
26832     },
26833     italic : {
26834         title: 'Italic (Ctrl+I)',
26835         text: 'Make the selected text italic.',
26836         cls: 'x-html-editor-tip'
26837     },
26838     ...
26839 </code></pre>
26840     * @type Object
26841      */
26842     buttonTips : {
26843         bold : {
26844             title: 'Bold (Ctrl+B)',
26845             text: 'Make the selected text bold.',
26846             cls: 'x-html-editor-tip'
26847         },
26848         italic : {
26849             title: 'Italic (Ctrl+I)',
26850             text: 'Make the selected text italic.',
26851             cls: 'x-html-editor-tip'
26852         },
26853         underline : {
26854             title: 'Underline (Ctrl+U)',
26855             text: 'Underline the selected text.',
26856             cls: 'x-html-editor-tip'
26857         },
26858         increasefontsize : {
26859             title: 'Grow Text',
26860             text: 'Increase the font size.',
26861             cls: 'x-html-editor-tip'
26862         },
26863         decreasefontsize : {
26864             title: 'Shrink Text',
26865             text: 'Decrease the font size.',
26866             cls: 'x-html-editor-tip'
26867         },
26868         backcolor : {
26869             title: 'Text Highlight Color',
26870             text: 'Change the background color of the selected text.',
26871             cls: 'x-html-editor-tip'
26872         },
26873         forecolor : {
26874             title: 'Font Color',
26875             text: 'Change the color of the selected text.',
26876             cls: 'x-html-editor-tip'
26877         },
26878         justifyleft : {
26879             title: 'Align Text Left',
26880             text: 'Align text to the left.',
26881             cls: 'x-html-editor-tip'
26882         },
26883         justifycenter : {
26884             title: 'Center Text',
26885             text: 'Center text in the editor.',
26886             cls: 'x-html-editor-tip'
26887         },
26888         justifyright : {
26889             title: 'Align Text Right',
26890             text: 'Align text to the right.',
26891             cls: 'x-html-editor-tip'
26892         },
26893         insertunorderedlist : {
26894             title: 'Bullet List',
26895             text: 'Start a bulleted list.',
26896             cls: 'x-html-editor-tip'
26897         },
26898         insertorderedlist : {
26899             title: 'Numbered List',
26900             text: 'Start a numbered list.',
26901             cls: 'x-html-editor-tip'
26902         },
26903         createlink : {
26904             title: 'Hyperlink',
26905             text: 'Make the selected text a hyperlink.',
26906             cls: 'x-html-editor-tip'
26907         },
26908         sourceedit : {
26909             title: 'Source Edit',
26910             text: 'Switch to source editing mode.',
26911             cls: 'x-html-editor-tip'
26912         }
26913     },
26914     // private
26915     onDestroy : function(){
26916         if(this.rendered){
26917             
26918             this.tb.items.each(function(item){
26919                 if(item.menu){
26920                     item.menu.removeAll();
26921                     if(item.menu.el){
26922                         item.menu.el.destroy();
26923                     }
26924                 }
26925                 item.destroy();
26926             });
26927              
26928         }
26929     },
26930     onFirstFocus: function() {
26931         this.tb.items.each(function(item){
26932            item.enable();
26933         });
26934     }
26935 });
26936
26937
26938
26939
26940 // <script type="text/javascript">
26941 /*
26942  * Based on
26943  * Ext JS Library 1.1.1
26944  * Copyright(c) 2006-2007, Ext JS, LLC.
26945  *  
26946  
26947  */
26948
26949  
26950 /**
26951  * @class Roo.form.HtmlEditor.ToolbarContext
26952  * Context Toolbar
26953  * 
26954  * Usage:
26955  *
26956  new Roo.form.HtmlEditor({
26957     ....
26958     toolbars : [
26959         { xtype: 'ToolbarStandard', styles : {} }
26960         { xtype: 'ToolbarContext', disable : {} }
26961     ]
26962 })
26963
26964      
26965  * 
26966  * @config : {Object} disable List of elements to disable.. (not done yet.)
26967  * @config : {Object} styles  Map of styles available.
26968  * 
26969  */
26970
26971 Roo.form.HtmlEditor.ToolbarContext = function(config)
26972 {
26973     
26974     Roo.apply(this, config);
26975     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26976     // dont call parent... till later.
26977     this.styles = this.styles || {};
26978 }
26979 Roo.form.HtmlEditor.ToolbarContext.types = {
26980     'IMG' : {
26981         width : {
26982             title: "Width",
26983             width: 40
26984         },
26985         height:  {
26986             title: "Height",
26987             width: 40
26988         },
26989         align: {
26990             title: "Align",
26991             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
26992             width : 80
26993             
26994         },
26995         border: {
26996             title: "Border",
26997             width: 40
26998         },
26999         alt: {
27000             title: "Alt",
27001             width: 120
27002         },
27003         src : {
27004             title: "Src",
27005             width: 220
27006         }
27007         
27008     },
27009     'A' : {
27010         name : {
27011             title: "Name",
27012             width: 50
27013         },
27014         href:  {
27015             title: "Href",
27016             width: 220
27017         } // border?
27018         
27019     },
27020     'TABLE' : {
27021         rows : {
27022             title: "Rows",
27023             width: 20
27024         },
27025         cols : {
27026             title: "Cols",
27027             width: 20
27028         },
27029         width : {
27030             title: "Width",
27031             width: 40
27032         },
27033         height : {
27034             title: "Height",
27035             width: 40
27036         },
27037         border : {
27038             title: "Border",
27039             width: 20
27040         }
27041     },
27042     'TD' : {
27043         width : {
27044             title: "Width",
27045             width: 40
27046         },
27047         height : {
27048             title: "Height",
27049             width: 40
27050         },   
27051         align: {
27052             title: "Align",
27053             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27054             width: 80
27055         },
27056         valign: {
27057             title: "Valign",
27058             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27059             width: 80
27060         },
27061         colspan: {
27062             title: "Colspan",
27063             width: 20
27064             
27065         }
27066     },
27067     'INPUT' : {
27068         name : {
27069             title: "name",
27070             width: 120
27071         },
27072         value : {
27073             title: "Value",
27074             width: 120
27075         },
27076         width : {
27077             title: "Width",
27078             width: 40
27079         }
27080     },
27081     'LABEL' : {
27082         'for' : {
27083             title: "For",
27084             width: 120
27085         }
27086     },
27087     'TEXTAREA' : {
27088           name : {
27089             title: "name",
27090             width: 120
27091         },
27092         rows : {
27093             title: "Rows",
27094             width: 20
27095         },
27096         cols : {
27097             title: "Cols",
27098             width: 20
27099         }
27100     },
27101     'SELECT' : {
27102         name : {
27103             title: "name",
27104             width: 120
27105         },
27106         selectoptions : {
27107             title: "Options",
27108             width: 200
27109         }
27110     },
27111     
27112     // should we really allow this??
27113     // should this just be 
27114     'BODY' : {
27115         title : {
27116             title: "title",
27117             width: 200,
27118             disabled : true
27119         }
27120     },
27121     '*' : {
27122         // empty..
27123     }
27124 };
27125
27126
27127
27128 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27129     
27130     tb: false,
27131     
27132     rendered: false,
27133     
27134     editor : false,
27135     /**
27136      * @cfg {Object} disable  List of toolbar elements to disable
27137          
27138      */
27139     disable : false,
27140     /**
27141      * @cfg {Object} styles List of styles 
27142      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27143      *
27144      * These must be defined in the page, so they get rendered correctly..
27145      * .headline { }
27146      * TD.underline { }
27147      * 
27148      */
27149     styles : false,
27150     
27151     
27152     
27153     toolbars : false,
27154     
27155     init : function(editor)
27156     {
27157         this.editor = editor;
27158         
27159         
27160         var fid = editor.frameId;
27161         var etb = this;
27162         function btn(id, toggle, handler){
27163             var xid = fid + '-'+ id ;
27164             return {
27165                 id : xid,
27166                 cmd : id,
27167                 cls : 'x-btn-icon x-edit-'+id,
27168                 enableToggle:toggle !== false,
27169                 scope: editor, // was editor...
27170                 handler:handler||editor.relayBtnCmd,
27171                 clickEvent:'mousedown',
27172                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27173                 tabIndex:-1
27174             };
27175         }
27176         // create a new element.
27177         var wdiv = editor.wrap.createChild({
27178                 tag: 'div'
27179             }, editor.wrap.dom.firstChild.nextSibling, true);
27180         
27181         // can we do this more than once??
27182         
27183          // stop form submits
27184       
27185  
27186         // disable everything...
27187         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27188         this.toolbars = {};
27189            
27190         for (var i in  ty) {
27191           
27192             this.toolbars[i] = this.buildToolbar(ty[i],i);
27193         }
27194         this.tb = this.toolbars.BODY;
27195         this.tb.el.show();
27196         this.buildFooter();
27197         this.footer.show();
27198         editor.on('hide', function( ) { this.footer.hide() }, this);
27199         editor.on('show', function( ) { this.footer.show() }, this);
27200         
27201          
27202         this.rendered = true;
27203         
27204         // the all the btns;
27205         editor.on('editorevent', this.updateToolbar, this);
27206         // other toolbars need to implement this..
27207         //editor.on('editmodechange', this.updateToolbar, this);
27208     },
27209     
27210     
27211     
27212     /**
27213      * Protected method that will not generally be called directly. It triggers
27214      * a toolbar update by reading the markup state of the current selection in the editor.
27215      */
27216     updateToolbar: function(editor,ev,sel){
27217
27218         //Roo.log(ev);
27219         // capture mouse up - this is handy for selecting images..
27220         // perhaps should go somewhere else...
27221         if(!this.editor.activated){
27222              this.editor.onFirstFocus();
27223             return;
27224         }
27225         
27226         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27227         // selectNode - might want to handle IE?
27228         if (ev &&
27229             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27230             ev.target && ev.target.tagName == 'IMG') {
27231             // they have click on an image...
27232             // let's see if we can change the selection...
27233             sel = ev.target;
27234          
27235               var nodeRange = sel.ownerDocument.createRange();
27236             try {
27237                 nodeRange.selectNode(sel);
27238             } catch (e) {
27239                 nodeRange.selectNodeContents(sel);
27240             }
27241             //nodeRange.collapse(true);
27242             var s = editor.win.getSelection();
27243             s.removeAllRanges();
27244             s.addRange(nodeRange);
27245         }  
27246         
27247       
27248         var updateFooter = sel ? false : true;
27249         
27250         
27251         var ans = this.editor.getAllAncestors();
27252         
27253         // pick
27254         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27255         
27256         if (!sel) { 
27257             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
27258             sel = sel ? sel : this.editor.doc.body;
27259             sel = sel.tagName.length ? sel : this.editor.doc.body;
27260             
27261         }
27262         // pick a menu that exists..
27263         var tn = sel.tagName.toUpperCase();
27264         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27265         
27266         tn = sel.tagName.toUpperCase();
27267         
27268         var lastSel = this.tb.selectedNode
27269         
27270         this.tb.selectedNode = sel;
27271         
27272         // if current menu does not match..
27273         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27274                 
27275             this.tb.el.hide();
27276             ///console.log("show: " + tn);
27277             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27278             this.tb.el.show();
27279             // update name
27280             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27281             
27282             
27283             // update attributes
27284             if (this.tb.fields) {
27285                 this.tb.fields.each(function(e) {
27286                    e.setValue(sel.getAttribute(e.attrname));
27287                 });
27288             }
27289             
27290             var hasStyles = false;
27291             for(var i in this.styles) {
27292                 hasStyles = true;
27293                 break;
27294             }
27295             
27296             // update styles
27297             if (hasStyles) { 
27298                 var st = this.tb.fields.item(0);
27299                 
27300                 st.store.removeAll();
27301                
27302                 
27303                 var cn = sel.className.split(/\s+/);
27304                 
27305                 var avs = [];
27306                 if (this.styles['*']) {
27307                     
27308                     Roo.each(this.styles['*'], function(v) {
27309                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27310                     });
27311                 }
27312                 if (this.styles[tn]) { 
27313                     Roo.each(this.styles[tn], function(v) {
27314                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27315                     });
27316                 }
27317                 
27318                 st.store.loadData(avs);
27319                 st.collapse();
27320                 st.setValue(cn);
27321             }
27322             // flag our selected Node.
27323             this.tb.selectedNode = sel;
27324            
27325            
27326             Roo.menu.MenuMgr.hideAll();
27327
27328         }
27329         
27330         if (!updateFooter) {
27331             return;
27332         }
27333         // update the footer
27334         //
27335         var html = '';
27336         
27337         this.footerEls = ans.reverse();
27338         Roo.each(this.footerEls, function(a,i) {
27339             if (!a) { return; }
27340             html += html.length ? ' &gt; '  :  '';
27341             
27342             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27343             
27344         });
27345        
27346         // 
27347         var sz = this.footDisp.up('td').getSize();
27348         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27349         this.footDisp.dom.style.marginLeft = '5px';
27350         
27351         this.footDisp.dom.style.overflow = 'hidden';
27352         
27353         this.footDisp.dom.innerHTML = html;
27354             
27355         //this.editorsyncValue();
27356     },
27357    
27358        
27359     // private
27360     onDestroy : function(){
27361         if(this.rendered){
27362             
27363             this.tb.items.each(function(item){
27364                 if(item.menu){
27365                     item.menu.removeAll();
27366                     if(item.menu.el){
27367                         item.menu.el.destroy();
27368                     }
27369                 }
27370                 item.destroy();
27371             });
27372              
27373         }
27374     },
27375     onFirstFocus: function() {
27376         // need to do this for all the toolbars..
27377         this.tb.items.each(function(item){
27378            item.enable();
27379         });
27380     },
27381     buildToolbar: function(tlist, nm)
27382     {
27383         var editor = this.editor;
27384          // create a new element.
27385         var wdiv = editor.wrap.createChild({
27386                 tag: 'div'
27387             }, editor.wrap.dom.firstChild.nextSibling, true);
27388         
27389        
27390         var tb = new Roo.Toolbar(wdiv);
27391         // add the name..
27392         
27393         tb.add(nm+ ":&nbsp;");
27394         
27395         var styles = [];
27396         for(var i in this.styles) {
27397             styles.push(i);
27398         }
27399         
27400         // styles...
27401         if (styles && styles.length) {
27402             
27403             // this needs a multi-select checkbox...
27404             tb.addField( new Roo.form.ComboBox({
27405                 store: new Roo.data.SimpleStore({
27406                     id : 'val',
27407                     fields: ['val', 'selected'],
27408                     data : [] 
27409                 }),
27410                 name : '-roo-edit-className',
27411                 attrname : 'className',
27412                 displayField:'val',
27413                 typeAhead: false,
27414                 mode: 'local',
27415                 editable : false,
27416                 triggerAction: 'all',
27417                 emptyText:'Select Style',
27418                 selectOnFocus:true,
27419                 width: 130,
27420                 listeners : {
27421                     'select': function(c, r, i) {
27422                         // initial support only for on class per el..
27423                         tb.selectedNode.className =  r ? r.get('val') : '';
27424                         editor.syncValue();
27425                     }
27426                 }
27427     
27428             }));
27429         }
27430             
27431         
27432         
27433         for (var i in tlist) {
27434             
27435             var item = tlist[i];
27436             tb.add(item.title + ":&nbsp;");
27437             
27438             
27439             
27440             
27441             if (item.opts) {
27442                 // opts == pulldown..
27443                 tb.addField( new Roo.form.ComboBox({
27444                     store: new Roo.data.SimpleStore({
27445                         id : 'val',
27446                         fields: ['val'],
27447                         data : item.opts  
27448                     }),
27449                     name : '-roo-edit-' + i,
27450                     attrname : i,
27451                     displayField:'val',
27452                     typeAhead: false,
27453                     mode: 'local',
27454                     editable : false,
27455                     triggerAction: 'all',
27456                     emptyText:'Select',
27457                     selectOnFocus:true,
27458                     width: item.width ? item.width  : 130,
27459                     listeners : {
27460                         'select': function(c, r, i) {
27461                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27462                         }
27463                     }
27464
27465                 }));
27466                 continue;
27467                     
27468                  
27469                 
27470                 tb.addField( new Roo.form.TextField({
27471                     name: i,
27472                     width: 100,
27473                     //allowBlank:false,
27474                     value: ''
27475                 }));
27476                 continue;
27477             }
27478             tb.addField( new Roo.form.TextField({
27479                 name: '-roo-edit-' + i,
27480                 attrname : i,
27481                 
27482                 width: item.width,
27483                 //allowBlank:true,
27484                 value: '',
27485                 listeners: {
27486                     'change' : function(f, nv, ov) {
27487                         tb.selectedNode.setAttribute(f.attrname, nv);
27488                     }
27489                 }
27490             }));
27491              
27492         }
27493         tb.el.on('click', function(e){
27494             e.preventDefault(); // what does this do?
27495         });
27496         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27497         tb.el.hide();
27498         tb.name = nm;
27499         // dont need to disable them... as they will get hidden
27500         return tb;
27501          
27502         
27503     },
27504     buildFooter : function()
27505     {
27506         
27507         var fel = this.editor.wrap.createChild();
27508         this.footer = new Roo.Toolbar(fel);
27509         // toolbar has scrolly on left / right?
27510         var footDisp= new Roo.Toolbar.Fill();
27511         var _t = this;
27512         this.footer.add(
27513             {
27514                 text : '&lt;',
27515                 xtype: 'Button',
27516                 handler : function() {
27517                     _t.footDisp.scrollTo('left',0,true)
27518                 }
27519             }
27520         );
27521         this.footer.add( footDisp );
27522         this.footer.add( 
27523             {
27524                 text : '&gt;',
27525                 xtype: 'Button',
27526                 handler : function() {
27527                     // no animation..
27528                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27529                 }
27530             }
27531         );
27532         var fel = Roo.get(footDisp.el);
27533         fel.addClass('x-editor-context');
27534         this.footDispWrap = fel; 
27535         this.footDispWrap.overflow  = 'hidden';
27536         
27537         this.footDisp = fel.createChild();
27538         this.footDispWrap.on('click', this.onContextClick, this)
27539         
27540         
27541     },
27542     onContextClick : function (ev,dom)
27543     {
27544         ev.preventDefault();
27545         var  cn = dom.className;
27546         Roo.log(cn);
27547         if (!cn.match(/x-ed-loc-/)) {
27548             return;
27549         }
27550         var n = cn.split('-').pop();
27551         var ans = this.footerEls;
27552         var sel = ans[n];
27553         
27554          // pick
27555         var range = this.editor.createRange();
27556         
27557         range.selectNodeContents(sel);
27558         //range.selectNode(sel);
27559         
27560         
27561         var selection = this.editor.getSelection();
27562         selection.removeAllRanges();
27563         selection.addRange(range);
27564         
27565         
27566         
27567         this.updateToolbar(null, null, sel);
27568         
27569         
27570     }
27571     
27572     
27573     
27574     
27575     
27576 });
27577
27578
27579
27580
27581
27582 /*
27583  * Based on:
27584  * Ext JS Library 1.1.1
27585  * Copyright(c) 2006-2007, Ext JS, LLC.
27586  *
27587  * Originally Released Under LGPL - original licence link has changed is not relivant.
27588  *
27589  * Fork - LGPL
27590  * <script type="text/javascript">
27591  */
27592  
27593 /**
27594  * @class Roo.form.BasicForm
27595  * @extends Roo.util.Observable
27596  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
27597  * @constructor
27598  * @param {String/HTMLElement/Roo.Element} el The form element or its id
27599  * @param {Object} config Configuration options
27600  */
27601 Roo.form.BasicForm = function(el, config){
27602     this.allItems = [];
27603     this.childForms = [];
27604     Roo.apply(this, config);
27605     /*
27606      * The Roo.form.Field items in this form.
27607      * @type MixedCollection
27608      */
27609      
27610      
27611     this.items = new Roo.util.MixedCollection(false, function(o){
27612         return o.id || (o.id = Roo.id());
27613     });
27614     this.addEvents({
27615         /**
27616          * @event beforeaction
27617          * Fires before any action is performed. Return false to cancel the action.
27618          * @param {Form} this
27619          * @param {Action} action The action to be performed
27620          */
27621         beforeaction: true,
27622         /**
27623          * @event actionfailed
27624          * Fires when an action fails.
27625          * @param {Form} this
27626          * @param {Action} action The action that failed
27627          */
27628         actionfailed : true,
27629         /**
27630          * @event actioncomplete
27631          * Fires when an action is completed.
27632          * @param {Form} this
27633          * @param {Action} action The action that completed
27634          */
27635         actioncomplete : true
27636     });
27637     if(el){
27638         this.initEl(el);
27639     }
27640     Roo.form.BasicForm.superclass.constructor.call(this);
27641 };
27642
27643 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
27644     /**
27645      * @cfg {String} method
27646      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
27647      */
27648     /**
27649      * @cfg {DataReader} reader
27650      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
27651      * This is optional as there is built-in support for processing JSON.
27652      */
27653     /**
27654      * @cfg {DataReader} errorReader
27655      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
27656      * This is completely optional as there is built-in support for processing JSON.
27657      */
27658     /**
27659      * @cfg {String} url
27660      * The URL to use for form actions if one isn't supplied in the action options.
27661      */
27662     /**
27663      * @cfg {Boolean} fileUpload
27664      * Set to true if this form is a file upload.
27665      */
27666      
27667     /**
27668      * @cfg {Object} baseParams
27669      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
27670      */
27671      /**
27672      
27673     /**
27674      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
27675      */
27676     timeout: 30,
27677
27678     // private
27679     activeAction : null,
27680
27681     /**
27682      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
27683      * or setValues() data instead of when the form was first created.
27684      */
27685     trackResetOnLoad : false,
27686     
27687     
27688     /**
27689      * childForms - used for multi-tab forms
27690      * @type {Array}
27691      */
27692     childForms : false,
27693     
27694     /**
27695      * allItems - full list of fields.
27696      * @type {Array}
27697      */
27698     allItems : false,
27699     
27700     /**
27701      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
27702      * element by passing it or its id or mask the form itself by passing in true.
27703      * @type Mixed
27704      */
27705     waitMsgTarget : false,
27706
27707     // private
27708     initEl : function(el){
27709         this.el = Roo.get(el);
27710         this.id = this.el.id || Roo.id();
27711         this.el.on('submit', this.onSubmit, this);
27712         this.el.addClass('x-form');
27713     },
27714
27715     // private
27716     onSubmit : function(e){
27717         e.stopEvent();
27718     },
27719
27720     /**
27721      * Returns true if client-side validation on the form is successful.
27722      * @return Boolean
27723      */
27724     isValid : function(){
27725         var valid = true;
27726         this.items.each(function(f){
27727            if(!f.validate()){
27728                valid = false;
27729            }
27730         });
27731         return valid;
27732     },
27733
27734     /**
27735      * Returns true if any fields in this form have changed since their original load.
27736      * @return Boolean
27737      */
27738     isDirty : function(){
27739         var dirty = false;
27740         this.items.each(function(f){
27741            if(f.isDirty()){
27742                dirty = true;
27743                return false;
27744            }
27745         });
27746         return dirty;
27747     },
27748
27749     /**
27750      * Performs a predefined action (submit or load) or custom actions you define on this form.
27751      * @param {String} actionName The name of the action type
27752      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
27753      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
27754      * accept other config options):
27755      * <pre>
27756 Property          Type             Description
27757 ----------------  ---------------  ----------------------------------------------------------------------------------
27758 url               String           The url for the action (defaults to the form's url)
27759 method            String           The form method to use (defaults to the form's method, or POST if not defined)
27760 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
27761 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
27762                                    validate the form on the client (defaults to false)
27763      * </pre>
27764      * @return {BasicForm} this
27765      */
27766     doAction : function(action, options){
27767         if(typeof action == 'string'){
27768             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
27769         }
27770         if(this.fireEvent('beforeaction', this, action) !== false){
27771             this.beforeAction(action);
27772             action.run.defer(100, action);
27773         }
27774         return this;
27775     },
27776
27777     /**
27778      * Shortcut to do a submit action.
27779      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27780      * @return {BasicForm} this
27781      */
27782     submit : function(options){
27783         this.doAction('submit', options);
27784         return this;
27785     },
27786
27787     /**
27788      * Shortcut to do a load action.
27789      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27790      * @return {BasicForm} this
27791      */
27792     load : function(options){
27793         this.doAction('load', options);
27794         return this;
27795     },
27796
27797     /**
27798      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
27799      * @param {Record} record The record to edit
27800      * @return {BasicForm} this
27801      */
27802     updateRecord : function(record){
27803         record.beginEdit();
27804         var fs = record.fields;
27805         fs.each(function(f){
27806             var field = this.findField(f.name);
27807             if(field){
27808                 record.set(f.name, field.getValue());
27809             }
27810         }, this);
27811         record.endEdit();
27812         return this;
27813     },
27814
27815     /**
27816      * Loads an Roo.data.Record into this form.
27817      * @param {Record} record The record to load
27818      * @return {BasicForm} this
27819      */
27820     loadRecord : function(record){
27821         this.setValues(record.data);
27822         return this;
27823     },
27824
27825     // private
27826     beforeAction : function(action){
27827         var o = action.options;
27828         
27829        
27830         if(this.waitMsgTarget === true){
27831             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
27832         }else if(this.waitMsgTarget){
27833             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
27834             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
27835         }else {
27836             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
27837         }
27838          
27839     },
27840
27841     // private
27842     afterAction : function(action, success){
27843         this.activeAction = null;
27844         var o = action.options;
27845         
27846         if(this.waitMsgTarget === true){
27847             this.el.unmask();
27848         }else if(this.waitMsgTarget){
27849             this.waitMsgTarget.unmask();
27850         }else{
27851             Roo.MessageBox.updateProgress(1);
27852             Roo.MessageBox.hide();
27853         }
27854          
27855         if(success){
27856             if(o.reset){
27857                 this.reset();
27858             }
27859             Roo.callback(o.success, o.scope, [this, action]);
27860             this.fireEvent('actioncomplete', this, action);
27861             
27862         }else{
27863             
27864             // failure condition..
27865             // we have a scenario where updates need confirming.
27866             // eg. if a locking scenario exists..
27867             // we look for { errors : { needs_confirm : true }} in the response.
27868             if (
27869                 (typeof(action.result) != 'undefined')  &&
27870                 (typeof(action.result.errors) != 'undefined')  &&
27871                 (typeof(action.result.errors.needs_confirm) != 'undefined')
27872            ){
27873                 var _t = this;
27874                 Roo.MessageBox.confirm(
27875                     "Change requires confirmation",
27876                     action.result.errorMsg,
27877                     function(r) {
27878                         if (r != 'yes') {
27879                             return;
27880                         }
27881                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
27882                     }
27883                     
27884                 );
27885                 
27886                 
27887                 
27888                 return;
27889             }
27890             
27891             Roo.callback(o.failure, o.scope, [this, action]);
27892             // show an error message if no failed handler is set..
27893             if (!this.hasListener('actionfailed')) {
27894                 Roo.MessageBox.alert("Error",
27895                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
27896                         action.result.errorMsg :
27897                         "Saving Failed, please check your entries or try again"
27898                 );
27899             }
27900             
27901             this.fireEvent('actionfailed', this, action);
27902         }
27903         
27904     },
27905
27906     /**
27907      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
27908      * @param {String} id The value to search for
27909      * @return Field
27910      */
27911     findField : function(id){
27912         var field = this.items.get(id);
27913         if(!field){
27914             this.items.each(function(f){
27915                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
27916                     field = f;
27917                     return false;
27918                 }
27919             });
27920         }
27921         return field || null;
27922     },
27923
27924     /**
27925      * Add a secondary form to this one, 
27926      * Used to provide tabbed forms. One form is primary, with hidden values 
27927      * which mirror the elements from the other forms.
27928      * 
27929      * @param {Roo.form.Form} form to add.
27930      * 
27931      */
27932     addForm : function(form)
27933     {
27934        
27935         if (this.childForms.indexOf(form) > -1) {
27936             // already added..
27937             return;
27938         }
27939         this.childForms.push(form);
27940         var n = '';
27941         Roo.each(form.allItems, function (fe) {
27942             
27943             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
27944             if (this.findField(n)) { // already added..
27945                 return;
27946             }
27947             var add = new Roo.form.Hidden({
27948                 name : n
27949             });
27950             add.render(this.el);
27951             
27952             this.add( add );
27953         }, this);
27954         
27955     },
27956     /**
27957      * Mark fields in this form invalid in bulk.
27958      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
27959      * @return {BasicForm} this
27960      */
27961     markInvalid : function(errors){
27962         if(errors instanceof Array){
27963             for(var i = 0, len = errors.length; i < len; i++){
27964                 var fieldError = errors[i];
27965                 var f = this.findField(fieldError.id);
27966                 if(f){
27967                     f.markInvalid(fieldError.msg);
27968                 }
27969             }
27970         }else{
27971             var field, id;
27972             for(id in errors){
27973                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
27974                     field.markInvalid(errors[id]);
27975                 }
27976             }
27977         }
27978         Roo.each(this.childForms || [], function (f) {
27979             f.markInvalid(errors);
27980         });
27981         
27982         return this;
27983     },
27984
27985     /**
27986      * Set values for fields in this form in bulk.
27987      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
27988      * @return {BasicForm} this
27989      */
27990     setValues : function(values){
27991         if(values instanceof Array){ // array of objects
27992             for(var i = 0, len = values.length; i < len; i++){
27993                 var v = values[i];
27994                 var f = this.findField(v.id);
27995                 if(f){
27996                     f.setValue(v.value);
27997                     if(this.trackResetOnLoad){
27998                         f.originalValue = f.getValue();
27999                     }
28000                 }
28001             }
28002         }else{ // object hash
28003             var field, id;
28004             for(id in values){
28005                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28006                     
28007                     if (field.setFromData && 
28008                         field.valueField && 
28009                         field.displayField &&
28010                         // combos' with local stores can 
28011                         // be queried via setValue()
28012                         // to set their value..
28013                         (field.store && !field.store.isLocal)
28014                         ) {
28015                         // it's a combo
28016                         var sd = { };
28017                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28018                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28019                         field.setFromData(sd);
28020                         
28021                     } else {
28022                         field.setValue(values[id]);
28023                     }
28024                     
28025                     
28026                     if(this.trackResetOnLoad){
28027                         field.originalValue = field.getValue();
28028                     }
28029                 }
28030             }
28031         }
28032          
28033         Roo.each(this.childForms || [], function (f) {
28034             f.setValues(values);
28035         });
28036                 
28037         return this;
28038     },
28039
28040     /**
28041      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28042      * they are returned as an array.
28043      * @param {Boolean} asString
28044      * @return {Object}
28045      */
28046     getValues : function(asString){
28047         if (this.childForms) {
28048             // copy values from the child forms
28049             Roo.each(this.childForms, function (f) {
28050                 this.setValues(f.getValues());
28051             }, this);
28052         }
28053         
28054         
28055         
28056         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28057         if(asString === true){
28058             return fs;
28059         }
28060         return Roo.urlDecode(fs);
28061     },
28062     
28063     /**
28064      * Returns the fields in this form as an object with key/value pairs. 
28065      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28066      * @return {Object}
28067      */
28068     getFieldValues : function(with_hidden)
28069     {
28070         if (this.childForms) {
28071             // copy values from the child forms
28072             // should this call getFieldValues - probably not as we do not currently copy
28073             // hidden fields when we generate..
28074             Roo.each(this.childForms, function (f) {
28075                 this.setValues(f.getValues());
28076             }, this);
28077         }
28078         
28079         var ret = {};
28080         this.items.each(function(f){
28081             if (!f.getName()) {
28082                 return;
28083             }
28084             var v = f.getValue();
28085             // not sure if this supported any more..
28086             if ((typeof(v) == 'object') && f.getRawValue) {
28087                 v = f.getRawValue() ; // dates..
28088             }
28089             // combo boxes where name != hiddenName...
28090             if (f.name != f.getName()) {
28091                 ret[f.name] = f.getRawValue();
28092             }
28093             ret[f.getName()] = v;
28094         });
28095         
28096         return ret;
28097     },
28098
28099     /**
28100      * Clears all invalid messages in this form.
28101      * @return {BasicForm} this
28102      */
28103     clearInvalid : function(){
28104         this.items.each(function(f){
28105            f.clearInvalid();
28106         });
28107         
28108         Roo.each(this.childForms || [], function (f) {
28109             f.clearInvalid();
28110         });
28111         
28112         
28113         return this;
28114     },
28115
28116     /**
28117      * Resets this form.
28118      * @return {BasicForm} this
28119      */
28120     reset : function(){
28121         this.items.each(function(f){
28122             f.reset();
28123         });
28124         
28125         Roo.each(this.childForms || [], function (f) {
28126             f.reset();
28127         });
28128        
28129         
28130         return this;
28131     },
28132
28133     /**
28134      * Add Roo.form components to this form.
28135      * @param {Field} field1
28136      * @param {Field} field2 (optional)
28137      * @param {Field} etc (optional)
28138      * @return {BasicForm} this
28139      */
28140     add : function(){
28141         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28142         return this;
28143     },
28144
28145
28146     /**
28147      * Removes a field from the items collection (does NOT remove its markup).
28148      * @param {Field} field
28149      * @return {BasicForm} this
28150      */
28151     remove : function(field){
28152         this.items.remove(field);
28153         return this;
28154     },
28155
28156     /**
28157      * Looks at the fields in this form, checks them for an id attribute,
28158      * and calls applyTo on the existing dom element with that id.
28159      * @return {BasicForm} this
28160      */
28161     render : function(){
28162         this.items.each(function(f){
28163             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28164                 f.applyTo(f.id);
28165             }
28166         });
28167         return this;
28168     },
28169
28170     /**
28171      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28172      * @param {Object} values
28173      * @return {BasicForm} this
28174      */
28175     applyToFields : function(o){
28176         this.items.each(function(f){
28177            Roo.apply(f, o);
28178         });
28179         return this;
28180     },
28181
28182     /**
28183      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28184      * @param {Object} values
28185      * @return {BasicForm} this
28186      */
28187     applyIfToFields : function(o){
28188         this.items.each(function(f){
28189            Roo.applyIf(f, o);
28190         });
28191         return this;
28192     }
28193 });
28194
28195 // back compat
28196 Roo.BasicForm = Roo.form.BasicForm;/*
28197  * Based on:
28198  * Ext JS Library 1.1.1
28199  * Copyright(c) 2006-2007, Ext JS, LLC.
28200  *
28201  * Originally Released Under LGPL - original licence link has changed is not relivant.
28202  *
28203  * Fork - LGPL
28204  * <script type="text/javascript">
28205  */
28206
28207 /**
28208  * @class Roo.form.Form
28209  * @extends Roo.form.BasicForm
28210  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28211  * @constructor
28212  * @param {Object} config Configuration options
28213  */
28214 Roo.form.Form = function(config){
28215     var xitems =  [];
28216     if (config.items) {
28217         xitems = config.items;
28218         delete config.items;
28219     }
28220    
28221     
28222     Roo.form.Form.superclass.constructor.call(this, null, config);
28223     this.url = this.url || this.action;
28224     if(!this.root){
28225         this.root = new Roo.form.Layout(Roo.applyIf({
28226             id: Roo.id()
28227         }, config));
28228     }
28229     this.active = this.root;
28230     /**
28231      * Array of all the buttons that have been added to this form via {@link addButton}
28232      * @type Array
28233      */
28234     this.buttons = [];
28235     this.allItems = [];
28236     this.addEvents({
28237         /**
28238          * @event clientvalidation
28239          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28240          * @param {Form} this
28241          * @param {Boolean} valid true if the form has passed client-side validation
28242          */
28243         clientvalidation: true,
28244         /**
28245          * @event rendered
28246          * Fires when the form is rendered
28247          * @param {Roo.form.Form} form
28248          */
28249         rendered : true
28250     });
28251     
28252     if (this.progressUrl) {
28253             // push a hidden field onto the list of fields..
28254             this.addxtype( {
28255                     xns: Roo.form, 
28256                     xtype : 'Hidden', 
28257                     name : 'UPLOAD_IDENTIFIER' 
28258             });
28259         }
28260         
28261     
28262     Roo.each(xitems, this.addxtype, this);
28263     
28264     
28265     
28266 };
28267
28268 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28269     /**
28270      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28271      */
28272     /**
28273      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28274      */
28275     /**
28276      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28277      */
28278     buttonAlign:'center',
28279
28280     /**
28281      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28282      */
28283     minButtonWidth:75,
28284
28285     /**
28286      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28287      * This property cascades to child containers if not set.
28288      */
28289     labelAlign:'left',
28290
28291     /**
28292      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28293      * fires a looping event with that state. This is required to bind buttons to the valid
28294      * state using the config value formBind:true on the button.
28295      */
28296     monitorValid : false,
28297
28298     /**
28299      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28300      */
28301     monitorPoll : 200,
28302     
28303     /**
28304      * @cfg {String} progressUrl - Url to return progress data 
28305      */
28306     
28307     progressUrl : false,
28308   
28309     /**
28310      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28311      * fields are added and the column is closed. If no fields are passed the column remains open
28312      * until end() is called.
28313      * @param {Object} config The config to pass to the column
28314      * @param {Field} field1 (optional)
28315      * @param {Field} field2 (optional)
28316      * @param {Field} etc (optional)
28317      * @return Column The column container object
28318      */
28319     column : function(c){
28320         var col = new Roo.form.Column(c);
28321         this.start(col);
28322         if(arguments.length > 1){ // duplicate code required because of Opera
28323             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28324             this.end();
28325         }
28326         return col;
28327     },
28328
28329     /**
28330      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28331      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28332      * until end() is called.
28333      * @param {Object} config The config to pass to the fieldset
28334      * @param {Field} field1 (optional)
28335      * @param {Field} field2 (optional)
28336      * @param {Field} etc (optional)
28337      * @return FieldSet The fieldset container object
28338      */
28339     fieldset : function(c){
28340         var fs = new Roo.form.FieldSet(c);
28341         this.start(fs);
28342         if(arguments.length > 1){ // duplicate code required because of Opera
28343             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28344             this.end();
28345         }
28346         return fs;
28347     },
28348
28349     /**
28350      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28351      * fields are added and the container is closed. If no fields are passed the container remains open
28352      * until end() is called.
28353      * @param {Object} config The config to pass to the Layout
28354      * @param {Field} field1 (optional)
28355      * @param {Field} field2 (optional)
28356      * @param {Field} etc (optional)
28357      * @return Layout The container object
28358      */
28359     container : function(c){
28360         var l = new Roo.form.Layout(c);
28361         this.start(l);
28362         if(arguments.length > 1){ // duplicate code required because of Opera
28363             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28364             this.end();
28365         }
28366         return l;
28367     },
28368
28369     /**
28370      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28371      * @param {Object} container A Roo.form.Layout or subclass of Layout
28372      * @return {Form} this
28373      */
28374     start : function(c){
28375         // cascade label info
28376         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28377         this.active.stack.push(c);
28378         c.ownerCt = this.active;
28379         this.active = c;
28380         return this;
28381     },
28382
28383     /**
28384      * Closes the current open container
28385      * @return {Form} this
28386      */
28387     end : function(){
28388         if(this.active == this.root){
28389             return this;
28390         }
28391         this.active = this.active.ownerCt;
28392         return this;
28393     },
28394
28395     /**
28396      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28397      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28398      * as the label of the field.
28399      * @param {Field} field1
28400      * @param {Field} field2 (optional)
28401      * @param {Field} etc. (optional)
28402      * @return {Form} this
28403      */
28404     add : function(){
28405         this.active.stack.push.apply(this.active.stack, arguments);
28406         this.allItems.push.apply(this.allItems,arguments);
28407         var r = [];
28408         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28409             if(a[i].isFormField){
28410                 r.push(a[i]);
28411             }
28412         }
28413         if(r.length > 0){
28414             Roo.form.Form.superclass.add.apply(this, r);
28415         }
28416         return this;
28417     },
28418     
28419
28420     
28421     
28422     
28423      /**
28424      * Find any element that has been added to a form, using it's ID or name
28425      * This can include framesets, columns etc. along with regular fields..
28426      * @param {String} id - id or name to find.
28427      
28428      * @return {Element} e - or false if nothing found.
28429      */
28430     findbyId : function(id)
28431     {
28432         var ret = false;
28433         if (!id) {
28434             return ret;
28435         }
28436         Roo.each(this.allItems, function(f){
28437             if (f.id == id || f.name == id ){
28438                 ret = f;
28439                 return false;
28440             }
28441         });
28442         return ret;
28443     },
28444
28445     
28446     
28447     /**
28448      * Render this form into the passed container. This should only be called once!
28449      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28450      * @return {Form} this
28451      */
28452     render : function(ct)
28453     {
28454         
28455         
28456         
28457         ct = Roo.get(ct);
28458         var o = this.autoCreate || {
28459             tag: 'form',
28460             method : this.method || 'POST',
28461             id : this.id || Roo.id()
28462         };
28463         this.initEl(ct.createChild(o));
28464
28465         this.root.render(this.el);
28466         
28467        
28468              
28469         this.items.each(function(f){
28470             f.render('x-form-el-'+f.id);
28471         });
28472
28473         if(this.buttons.length > 0){
28474             // tables are required to maintain order and for correct IE layout
28475             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28476                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28477                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28478             }}, null, true);
28479             var tr = tb.getElementsByTagName('tr')[0];
28480             for(var i = 0, len = this.buttons.length; i < len; i++) {
28481                 var b = this.buttons[i];
28482                 var td = document.createElement('td');
28483                 td.className = 'x-form-btn-td';
28484                 b.render(tr.appendChild(td));
28485             }
28486         }
28487         if(this.monitorValid){ // initialize after render
28488             this.startMonitoring();
28489         }
28490         this.fireEvent('rendered', this);
28491         return this;
28492     },
28493
28494     /**
28495      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28496      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28497      * object or a valid Roo.DomHelper element config
28498      * @param {Function} handler The function called when the button is clicked
28499      * @param {Object} scope (optional) The scope of the handler function
28500      * @return {Roo.Button}
28501      */
28502     addButton : function(config, handler, scope){
28503         var bc = {
28504             handler: handler,
28505             scope: scope,
28506             minWidth: this.minButtonWidth,
28507             hideParent:true
28508         };
28509         if(typeof config == "string"){
28510             bc.text = config;
28511         }else{
28512             Roo.apply(bc, config);
28513         }
28514         var btn = new Roo.Button(null, bc);
28515         this.buttons.push(btn);
28516         return btn;
28517     },
28518
28519      /**
28520      * Adds a series of form elements (using the xtype property as the factory method.
28521      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28522      * @param {Object} config 
28523      */
28524     
28525     addxtype : function()
28526     {
28527         var ar = Array.prototype.slice.call(arguments, 0);
28528         var ret = false;
28529         for(var i = 0; i < ar.length; i++) {
28530             if (!ar[i]) {
28531                 continue; // skip -- if this happends something invalid got sent, we 
28532                 // should ignore it, as basically that interface element will not show up
28533                 // and that should be pretty obvious!!
28534             }
28535             
28536             if (Roo.form[ar[i].xtype]) {
28537                 ar[i].form = this;
28538                 var fe = Roo.factory(ar[i], Roo.form);
28539                 if (!ret) {
28540                     ret = fe;
28541                 }
28542                 fe.form = this;
28543                 if (fe.store) {
28544                     fe.store.form = this;
28545                 }
28546                 if (fe.isLayout) {  
28547                          
28548                     this.start(fe);
28549                     this.allItems.push(fe);
28550                     if (fe.items && fe.addxtype) {
28551                         fe.addxtype.apply(fe, fe.items);
28552                         delete fe.items;
28553                     }
28554                      this.end();
28555                     continue;
28556                 }
28557                 
28558                 
28559                  
28560                 this.add(fe);
28561               //  console.log('adding ' + ar[i].xtype);
28562             }
28563             if (ar[i].xtype == 'Button') {  
28564                 //console.log('adding button');
28565                 //console.log(ar[i]);
28566                 this.addButton(ar[i]);
28567                 this.allItems.push(fe);
28568                 continue;
28569             }
28570             
28571             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
28572                 alert('end is not supported on xtype any more, use items');
28573             //    this.end();
28574             //    //console.log('adding end');
28575             }
28576             
28577         }
28578         return ret;
28579     },
28580     
28581     /**
28582      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
28583      * option "monitorValid"
28584      */
28585     startMonitoring : function(){
28586         if(!this.bound){
28587             this.bound = true;
28588             Roo.TaskMgr.start({
28589                 run : this.bindHandler,
28590                 interval : this.monitorPoll || 200,
28591                 scope: this
28592             });
28593         }
28594     },
28595
28596     /**
28597      * Stops monitoring of the valid state of this form
28598      */
28599     stopMonitoring : function(){
28600         this.bound = false;
28601     },
28602
28603     // private
28604     bindHandler : function(){
28605         if(!this.bound){
28606             return false; // stops binding
28607         }
28608         var valid = true;
28609         this.items.each(function(f){
28610             if(!f.isValid(true)){
28611                 valid = false;
28612                 return false;
28613             }
28614         });
28615         for(var i = 0, len = this.buttons.length; i < len; i++){
28616             var btn = this.buttons[i];
28617             if(btn.formBind === true && btn.disabled === valid){
28618                 btn.setDisabled(!valid);
28619             }
28620         }
28621         this.fireEvent('clientvalidation', this, valid);
28622     }
28623     
28624     
28625     
28626     
28627     
28628     
28629     
28630     
28631 });
28632
28633
28634 // back compat
28635 Roo.Form = Roo.form.Form;
28636 /*
28637  * Based on:
28638  * Ext JS Library 1.1.1
28639  * Copyright(c) 2006-2007, Ext JS, LLC.
28640  *
28641  * Originally Released Under LGPL - original licence link has changed is not relivant.
28642  *
28643  * Fork - LGPL
28644  * <script type="text/javascript">
28645  */
28646  
28647  /**
28648  * @class Roo.form.Action
28649  * Internal Class used to handle form actions
28650  * @constructor
28651  * @param {Roo.form.BasicForm} el The form element or its id
28652  * @param {Object} config Configuration options
28653  */
28654  
28655  
28656 // define the action interface
28657 Roo.form.Action = function(form, options){
28658     this.form = form;
28659     this.options = options || {};
28660 };
28661 /**
28662  * Client Validation Failed
28663  * @const 
28664  */
28665 Roo.form.Action.CLIENT_INVALID = 'client';
28666 /**
28667  * Server Validation Failed
28668  * @const 
28669  */
28670  Roo.form.Action.SERVER_INVALID = 'server';
28671  /**
28672  * Connect to Server Failed
28673  * @const 
28674  */
28675 Roo.form.Action.CONNECT_FAILURE = 'connect';
28676 /**
28677  * Reading Data from Server Failed
28678  * @const 
28679  */
28680 Roo.form.Action.LOAD_FAILURE = 'load';
28681
28682 Roo.form.Action.prototype = {
28683     type : 'default',
28684     failureType : undefined,
28685     response : undefined,
28686     result : undefined,
28687
28688     // interface method
28689     run : function(options){
28690
28691     },
28692
28693     // interface method
28694     success : function(response){
28695
28696     },
28697
28698     // interface method
28699     handleResponse : function(response){
28700
28701     },
28702
28703     // default connection failure
28704     failure : function(response){
28705         
28706         this.response = response;
28707         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28708         this.form.afterAction(this, false);
28709     },
28710
28711     processResponse : function(response){
28712         this.response = response;
28713         if(!response.responseText){
28714             return true;
28715         }
28716         this.result = this.handleResponse(response);
28717         return this.result;
28718     },
28719
28720     // utility functions used internally
28721     getUrl : function(appendParams){
28722         var url = this.options.url || this.form.url || this.form.el.dom.action;
28723         if(appendParams){
28724             var p = this.getParams();
28725             if(p){
28726                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
28727             }
28728         }
28729         return url;
28730     },
28731
28732     getMethod : function(){
28733         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
28734     },
28735
28736     getParams : function(){
28737         var bp = this.form.baseParams;
28738         var p = this.options.params;
28739         if(p){
28740             if(typeof p == "object"){
28741                 p = Roo.urlEncode(Roo.applyIf(p, bp));
28742             }else if(typeof p == 'string' && bp){
28743                 p += '&' + Roo.urlEncode(bp);
28744             }
28745         }else if(bp){
28746             p = Roo.urlEncode(bp);
28747         }
28748         return p;
28749     },
28750
28751     createCallback : function(){
28752         return {
28753             success: this.success,
28754             failure: this.failure,
28755             scope: this,
28756             timeout: (this.form.timeout*1000),
28757             upload: this.form.fileUpload ? this.success : undefined
28758         };
28759     }
28760 };
28761
28762 Roo.form.Action.Submit = function(form, options){
28763     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
28764 };
28765
28766 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
28767     type : 'submit',
28768
28769     haveProgress : false,
28770     uploadComplete : false,
28771     
28772     // uploadProgress indicator.
28773     uploadProgress : function()
28774     {
28775         if (!this.form.progressUrl) {
28776             return;
28777         }
28778         
28779         if (!this.haveProgress) {
28780             Roo.MessageBox.progress("Uploading", "Uploading");
28781         }
28782         if (this.uploadComplete) {
28783            Roo.MessageBox.hide();
28784            return;
28785         }
28786         
28787         this.haveProgress = true;
28788    
28789         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
28790         
28791         var c = new Roo.data.Connection();
28792         c.request({
28793             url : this.form.progressUrl,
28794             params: {
28795                 id : uid
28796             },
28797             method: 'GET',
28798             success : function(req){
28799                //console.log(data);
28800                 var rdata = false;
28801                 var edata;
28802                 try  {
28803                    rdata = Roo.decode(req.responseText)
28804                 } catch (e) {
28805                     Roo.log("Invalid data from server..");
28806                     Roo.log(edata);
28807                     return;
28808                 }
28809                 if (!rdata || !rdata.success) {
28810                     Roo.log(rdata);
28811                     Roo.MessageBox.alert(Roo.encode(rdata));
28812                     return;
28813                 }
28814                 var data = rdata.data;
28815                 
28816                 if (this.uploadComplete) {
28817                    Roo.MessageBox.hide();
28818                    return;
28819                 }
28820                    
28821                 if (data){
28822                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
28823                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
28824                     );
28825                 }
28826                 this.uploadProgress.defer(2000,this);
28827             },
28828        
28829             failure: function(data) {
28830                 Roo.log('progress url failed ');
28831                 Roo.log(data);
28832             },
28833             scope : this
28834         });
28835            
28836     },
28837     
28838     
28839     run : function()
28840     {
28841         // run get Values on the form, so it syncs any secondary forms.
28842         this.form.getValues();
28843         
28844         var o = this.options;
28845         var method = this.getMethod();
28846         var isPost = method == 'POST';
28847         if(o.clientValidation === false || this.form.isValid()){
28848             
28849             if (this.form.progressUrl) {
28850                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
28851                     (new Date() * 1) + '' + Math.random());
28852                     
28853             } 
28854             
28855             
28856             Roo.Ajax.request(Roo.apply(this.createCallback(), {
28857                 form:this.form.el.dom,
28858                 url:this.getUrl(!isPost),
28859                 method: method,
28860                 params:isPost ? this.getParams() : null,
28861                 isUpload: this.form.fileUpload
28862             }));
28863             
28864             this.uploadProgress();
28865
28866         }else if (o.clientValidation !== false){ // client validation failed
28867             this.failureType = Roo.form.Action.CLIENT_INVALID;
28868             this.form.afterAction(this, false);
28869         }
28870     },
28871
28872     success : function(response)
28873     {
28874         this.uploadComplete= true;
28875         if (this.haveProgress) {
28876             Roo.MessageBox.hide();
28877         }
28878         
28879         
28880         var result = this.processResponse(response);
28881         if(result === true || result.success){
28882             this.form.afterAction(this, true);
28883             return;
28884         }
28885         if(result.errors){
28886             this.form.markInvalid(result.errors);
28887             this.failureType = Roo.form.Action.SERVER_INVALID;
28888         }
28889         this.form.afterAction(this, false);
28890     },
28891     failure : function(response)
28892     {
28893         this.uploadComplete= true;
28894         if (this.haveProgress) {
28895             Roo.MessageBox.hide();
28896         }
28897         
28898         this.response = response;
28899         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28900         this.form.afterAction(this, false);
28901     },
28902     
28903     handleResponse : function(response){
28904         if(this.form.errorReader){
28905             var rs = this.form.errorReader.read(response);
28906             var errors = [];
28907             if(rs.records){
28908                 for(var i = 0, len = rs.records.length; i < len; i++) {
28909                     var r = rs.records[i];
28910                     errors[i] = r.data;
28911                 }
28912             }
28913             if(errors.length < 1){
28914                 errors = null;
28915             }
28916             return {
28917                 success : rs.success,
28918                 errors : errors
28919             };
28920         }
28921         var ret = false;
28922         try {
28923             ret = Roo.decode(response.responseText);
28924         } catch (e) {
28925             ret = {
28926                 success: false,
28927                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
28928                 errors : []
28929             };
28930         }
28931         return ret;
28932         
28933     }
28934 });
28935
28936
28937 Roo.form.Action.Load = function(form, options){
28938     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
28939     this.reader = this.form.reader;
28940 };
28941
28942 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
28943     type : 'load',
28944
28945     run : function(){
28946         
28947         Roo.Ajax.request(Roo.apply(
28948                 this.createCallback(), {
28949                     method:this.getMethod(),
28950                     url:this.getUrl(false),
28951                     params:this.getParams()
28952         }));
28953     },
28954
28955     success : function(response){
28956         
28957         var result = this.processResponse(response);
28958         if(result === true || !result.success || !result.data){
28959             this.failureType = Roo.form.Action.LOAD_FAILURE;
28960             this.form.afterAction(this, false);
28961             return;
28962         }
28963         this.form.clearInvalid();
28964         this.form.setValues(result.data);
28965         this.form.afterAction(this, true);
28966     },
28967
28968     handleResponse : function(response){
28969         if(this.form.reader){
28970             var rs = this.form.reader.read(response);
28971             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
28972             return {
28973                 success : rs.success,
28974                 data : data
28975             };
28976         }
28977         return Roo.decode(response.responseText);
28978     }
28979 });
28980
28981 Roo.form.Action.ACTION_TYPES = {
28982     'load' : Roo.form.Action.Load,
28983     'submit' : Roo.form.Action.Submit
28984 };/*
28985  * Based on:
28986  * Ext JS Library 1.1.1
28987  * Copyright(c) 2006-2007, Ext JS, LLC.
28988  *
28989  * Originally Released Under LGPL - original licence link has changed is not relivant.
28990  *
28991  * Fork - LGPL
28992  * <script type="text/javascript">
28993  */
28994  
28995 /**
28996  * @class Roo.form.Layout
28997  * @extends Roo.Component
28998  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
28999  * @constructor
29000  * @param {Object} config Configuration options
29001  */
29002 Roo.form.Layout = function(config){
29003     var xitems = [];
29004     if (config.items) {
29005         xitems = config.items;
29006         delete config.items;
29007     }
29008     Roo.form.Layout.superclass.constructor.call(this, config);
29009     this.stack = [];
29010     Roo.each(xitems, this.addxtype, this);
29011      
29012 };
29013
29014 Roo.extend(Roo.form.Layout, Roo.Component, {
29015     /**
29016      * @cfg {String/Object} autoCreate
29017      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29018      */
29019     /**
29020      * @cfg {String/Object/Function} style
29021      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29022      * a function which returns such a specification.
29023      */
29024     /**
29025      * @cfg {String} labelAlign
29026      * Valid values are "left," "top" and "right" (defaults to "left")
29027      */
29028     /**
29029      * @cfg {Number} labelWidth
29030      * Fixed width in pixels of all field labels (defaults to undefined)
29031      */
29032     /**
29033      * @cfg {Boolean} clear
29034      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29035      */
29036     clear : true,
29037     /**
29038      * @cfg {String} labelSeparator
29039      * The separator to use after field labels (defaults to ':')
29040      */
29041     labelSeparator : ':',
29042     /**
29043      * @cfg {Boolean} hideLabels
29044      * True to suppress the display of field labels in this layout (defaults to false)
29045      */
29046     hideLabels : false,
29047
29048     // private
29049     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29050     
29051     isLayout : true,
29052     
29053     // private
29054     onRender : function(ct, position){
29055         if(this.el){ // from markup
29056             this.el = Roo.get(this.el);
29057         }else {  // generate
29058             var cfg = this.getAutoCreate();
29059             this.el = ct.createChild(cfg, position);
29060         }
29061         if(this.style){
29062             this.el.applyStyles(this.style);
29063         }
29064         if(this.labelAlign){
29065             this.el.addClass('x-form-label-'+this.labelAlign);
29066         }
29067         if(this.hideLabels){
29068             this.labelStyle = "display:none";
29069             this.elementStyle = "padding-left:0;";
29070         }else{
29071             if(typeof this.labelWidth == 'number'){
29072                 this.labelStyle = "width:"+this.labelWidth+"px;";
29073                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29074             }
29075             if(this.labelAlign == 'top'){
29076                 this.labelStyle = "width:auto;";
29077                 this.elementStyle = "padding-left:0;";
29078             }
29079         }
29080         var stack = this.stack;
29081         var slen = stack.length;
29082         if(slen > 0){
29083             if(!this.fieldTpl){
29084                 var t = new Roo.Template(
29085                     '<div class="x-form-item {5}">',
29086                         '<label for="{0}" style="{2}">{1}{4}</label>',
29087                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29088                         '</div>',
29089                     '</div><div class="x-form-clear-left"></div>'
29090                 );
29091                 t.disableFormats = true;
29092                 t.compile();
29093                 Roo.form.Layout.prototype.fieldTpl = t;
29094             }
29095             for(var i = 0; i < slen; i++) {
29096                 if(stack[i].isFormField){
29097                     this.renderField(stack[i]);
29098                 }else{
29099                     this.renderComponent(stack[i]);
29100                 }
29101             }
29102         }
29103         if(this.clear){
29104             this.el.createChild({cls:'x-form-clear'});
29105         }
29106     },
29107
29108     // private
29109     renderField : function(f){
29110         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29111                f.id, //0
29112                f.fieldLabel, //1
29113                f.labelStyle||this.labelStyle||'', //2
29114                this.elementStyle||'', //3
29115                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29116                f.itemCls||this.itemCls||''  //5
29117        ], true).getPrevSibling());
29118     },
29119
29120     // private
29121     renderComponent : function(c){
29122         c.render(c.isLayout ? this.el : this.el.createChild());    
29123     },
29124     /**
29125      * Adds a object form elements (using the xtype property as the factory method.)
29126      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29127      * @param {Object} config 
29128      */
29129     addxtype : function(o)
29130     {
29131         // create the lement.
29132         o.form = this.form;
29133         var fe = Roo.factory(o, Roo.form);
29134         this.form.allItems.push(fe);
29135         this.stack.push(fe);
29136         
29137         if (fe.isFormField) {
29138             this.form.items.add(fe);
29139         }
29140          
29141         return fe;
29142     }
29143 });
29144
29145 /**
29146  * @class Roo.form.Column
29147  * @extends Roo.form.Layout
29148  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29149  * @constructor
29150  * @param {Object} config Configuration options
29151  */
29152 Roo.form.Column = function(config){
29153     Roo.form.Column.superclass.constructor.call(this, config);
29154 };
29155
29156 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29157     /**
29158      * @cfg {Number/String} width
29159      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29160      */
29161     /**
29162      * @cfg {String/Object} autoCreate
29163      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29164      */
29165
29166     // private
29167     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29168
29169     // private
29170     onRender : function(ct, position){
29171         Roo.form.Column.superclass.onRender.call(this, ct, position);
29172         if(this.width){
29173             this.el.setWidth(this.width);
29174         }
29175     }
29176 });
29177
29178
29179 /**
29180  * @class Roo.form.Row
29181  * @extends Roo.form.Layout
29182  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29183  * @constructor
29184  * @param {Object} config Configuration options
29185  */
29186
29187  
29188 Roo.form.Row = function(config){
29189     Roo.form.Row.superclass.constructor.call(this, config);
29190 };
29191  
29192 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29193       /**
29194      * @cfg {Number/String} width
29195      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29196      */
29197     /**
29198      * @cfg {Number/String} height
29199      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29200      */
29201     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29202     
29203     padWidth : 20,
29204     // private
29205     onRender : function(ct, position){
29206         //console.log('row render');
29207         if(!this.rowTpl){
29208             var t = new Roo.Template(
29209                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29210                     '<label for="{0}" style="{2}">{1}{4}</label>',
29211                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29212                     '</div>',
29213                 '</div>'
29214             );
29215             t.disableFormats = true;
29216             t.compile();
29217             Roo.form.Layout.prototype.rowTpl = t;
29218         }
29219         this.fieldTpl = this.rowTpl;
29220         
29221         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29222         var labelWidth = 100;
29223         
29224         if ((this.labelAlign != 'top')) {
29225             if (typeof this.labelWidth == 'number') {
29226                 labelWidth = this.labelWidth
29227             }
29228             this.padWidth =  20 + labelWidth;
29229             
29230         }
29231         
29232         Roo.form.Column.superclass.onRender.call(this, ct, position);
29233         if(this.width){
29234             this.el.setWidth(this.width);
29235         }
29236         if(this.height){
29237             this.el.setHeight(this.height);
29238         }
29239     },
29240     
29241     // private
29242     renderField : function(f){
29243         f.fieldEl = this.fieldTpl.append(this.el, [
29244                f.id, f.fieldLabel,
29245                f.labelStyle||this.labelStyle||'',
29246                this.elementStyle||'',
29247                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29248                f.itemCls||this.itemCls||'',
29249                f.width ? f.width + this.padWidth : 160 + this.padWidth
29250        ],true);
29251     }
29252 });
29253  
29254
29255 /**
29256  * @class Roo.form.FieldSet
29257  * @extends Roo.form.Layout
29258  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29259  * @constructor
29260  * @param {Object} config Configuration options
29261  */
29262 Roo.form.FieldSet = function(config){
29263     Roo.form.FieldSet.superclass.constructor.call(this, config);
29264 };
29265
29266 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29267     /**
29268      * @cfg {String} legend
29269      * The text to display as the legend for the FieldSet (defaults to '')
29270      */
29271     /**
29272      * @cfg {String/Object} autoCreate
29273      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29274      */
29275
29276     // private
29277     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29278
29279     // private
29280     onRender : function(ct, position){
29281         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29282         if(this.legend){
29283             this.setLegend(this.legend);
29284         }
29285     },
29286
29287     // private
29288     setLegend : function(text){
29289         if(this.rendered){
29290             this.el.child('legend').update(text);
29291         }
29292     }
29293 });/*
29294  * Based on:
29295  * Ext JS Library 1.1.1
29296  * Copyright(c) 2006-2007, Ext JS, LLC.
29297  *
29298  * Originally Released Under LGPL - original licence link has changed is not relivant.
29299  *
29300  * Fork - LGPL
29301  * <script type="text/javascript">
29302  */
29303 /**
29304  * @class Roo.form.VTypes
29305  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29306  * @singleton
29307  */
29308 Roo.form.VTypes = function(){
29309     // closure these in so they are only created once.
29310     var alpha = /^[a-zA-Z_]+$/;
29311     var alphanum = /^[a-zA-Z0-9_]+$/;
29312     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29313     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29314
29315     // All these messages and functions are configurable
29316     return {
29317         /**
29318          * The function used to validate email addresses
29319          * @param {String} value The email address
29320          */
29321         'email' : function(v){
29322             return email.test(v);
29323         },
29324         /**
29325          * The error text to display when the email validation function returns false
29326          * @type String
29327          */
29328         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29329         /**
29330          * The keystroke filter mask to be applied on email input
29331          * @type RegExp
29332          */
29333         'emailMask' : /[a-z0-9_\.\-@]/i,
29334
29335         /**
29336          * The function used to validate URLs
29337          * @param {String} value The URL
29338          */
29339         'url' : function(v){
29340             return url.test(v);
29341         },
29342         /**
29343          * The error text to display when the url validation function returns false
29344          * @type String
29345          */
29346         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29347         
29348         /**
29349          * The function used to validate alpha values
29350          * @param {String} value The value
29351          */
29352         'alpha' : function(v){
29353             return alpha.test(v);
29354         },
29355         /**
29356          * The error text to display when the alpha validation function returns false
29357          * @type String
29358          */
29359         'alphaText' : 'This field should only contain letters and _',
29360         /**
29361          * The keystroke filter mask to be applied on alpha input
29362          * @type RegExp
29363          */
29364         'alphaMask' : /[a-z_]/i,
29365
29366         /**
29367          * The function used to validate alphanumeric values
29368          * @param {String} value The value
29369          */
29370         'alphanum' : function(v){
29371             return alphanum.test(v);
29372         },
29373         /**
29374          * The error text to display when the alphanumeric validation function returns false
29375          * @type String
29376          */
29377         'alphanumText' : 'This field should only contain letters, numbers and _',
29378         /**
29379          * The keystroke filter mask to be applied on alphanumeric input
29380          * @type RegExp
29381          */
29382         'alphanumMask' : /[a-z0-9_]/i
29383     };
29384 }();//<script type="text/javascript">
29385
29386 /**
29387  * @class Roo.form.FCKeditor
29388  * @extends Roo.form.TextArea
29389  * Wrapper around the FCKEditor http://www.fckeditor.net
29390  * @constructor
29391  * Creates a new FCKeditor
29392  * @param {Object} config Configuration options
29393  */
29394 Roo.form.FCKeditor = function(config){
29395     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29396     this.addEvents({
29397          /**
29398          * @event editorinit
29399          * Fired when the editor is initialized - you can add extra handlers here..
29400          * @param {FCKeditor} this
29401          * @param {Object} the FCK object.
29402          */
29403         editorinit : true
29404     });
29405     
29406     
29407 };
29408 Roo.form.FCKeditor.editors = { };
29409 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29410 {
29411     //defaultAutoCreate : {
29412     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29413     //},
29414     // private
29415     /**
29416      * @cfg {Object} fck options - see fck manual for details.
29417      */
29418     fckconfig : false,
29419     
29420     /**
29421      * @cfg {Object} fck toolbar set (Basic or Default)
29422      */
29423     toolbarSet : 'Basic',
29424     /**
29425      * @cfg {Object} fck BasePath
29426      */ 
29427     basePath : '/fckeditor/',
29428     
29429     
29430     frame : false,
29431     
29432     value : '',
29433     
29434    
29435     onRender : function(ct, position)
29436     {
29437         if(!this.el){
29438             this.defaultAutoCreate = {
29439                 tag: "textarea",
29440                 style:"width:300px;height:60px;",
29441                 autocomplete: "off"
29442             };
29443         }
29444         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29445         /*
29446         if(this.grow){
29447             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29448             if(this.preventScrollbars){
29449                 this.el.setStyle("overflow", "hidden");
29450             }
29451             this.el.setHeight(this.growMin);
29452         }
29453         */
29454         //console.log('onrender' + this.getId() );
29455         Roo.form.FCKeditor.editors[this.getId()] = this;
29456          
29457
29458         this.replaceTextarea() ;
29459         
29460     },
29461     
29462     getEditor : function() {
29463         return this.fckEditor;
29464     },
29465     /**
29466      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29467      * @param {Mixed} value The value to set
29468      */
29469     
29470     
29471     setValue : function(value)
29472     {
29473         //console.log('setValue: ' + value);
29474         
29475         if(typeof(value) == 'undefined') { // not sure why this is happending...
29476             return;
29477         }
29478         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29479         
29480         //if(!this.el || !this.getEditor()) {
29481         //    this.value = value;
29482             //this.setValue.defer(100,this,[value]);    
29483         //    return;
29484         //} 
29485         
29486         if(!this.getEditor()) {
29487             return;
29488         }
29489         
29490         this.getEditor().SetData(value);
29491         
29492         //
29493
29494     },
29495
29496     /**
29497      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29498      * @return {Mixed} value The field value
29499      */
29500     getValue : function()
29501     {
29502         
29503         if (this.frame && this.frame.dom.style.display == 'none') {
29504             return Roo.form.FCKeditor.superclass.getValue.call(this);
29505         }
29506         
29507         if(!this.el || !this.getEditor()) {
29508            
29509            // this.getValue.defer(100,this); 
29510             return this.value;
29511         }
29512        
29513         
29514         var value=this.getEditor().GetData();
29515         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29516         return Roo.form.FCKeditor.superclass.getValue.call(this);
29517         
29518
29519     },
29520
29521     /**
29522      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
29523      * @return {Mixed} value The field value
29524      */
29525     getRawValue : function()
29526     {
29527         if (this.frame && this.frame.dom.style.display == 'none') {
29528             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29529         }
29530         
29531         if(!this.el || !this.getEditor()) {
29532             //this.getRawValue.defer(100,this); 
29533             return this.value;
29534             return;
29535         }
29536         
29537         
29538         
29539         var value=this.getEditor().GetData();
29540         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29541         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29542          
29543     },
29544     
29545     setSize : function(w,h) {
29546         
29547         
29548         
29549         //if (this.frame && this.frame.dom.style.display == 'none') {
29550         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29551         //    return;
29552         //}
29553         //if(!this.el || !this.getEditor()) {
29554         //    this.setSize.defer(100,this, [w,h]); 
29555         //    return;
29556         //}
29557         
29558         
29559         
29560         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29561         
29562         this.frame.dom.setAttribute('width', w);
29563         this.frame.dom.setAttribute('height', h);
29564         this.frame.setSize(w,h);
29565         
29566     },
29567     
29568     toggleSourceEdit : function(value) {
29569         
29570       
29571          
29572         this.el.dom.style.display = value ? '' : 'none';
29573         this.frame.dom.style.display = value ?  'none' : '';
29574         
29575     },
29576     
29577     
29578     focus: function(tag)
29579     {
29580         if (this.frame.dom.style.display == 'none') {
29581             return Roo.form.FCKeditor.superclass.focus.call(this);
29582         }
29583         if(!this.el || !this.getEditor()) {
29584             this.focus.defer(100,this, [tag]); 
29585             return;
29586         }
29587         
29588         
29589         
29590         
29591         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
29592         this.getEditor().Focus();
29593         if (tgs.length) {
29594             if (!this.getEditor().Selection.GetSelection()) {
29595                 this.focus.defer(100,this, [tag]); 
29596                 return;
29597             }
29598             
29599             
29600             var r = this.getEditor().EditorDocument.createRange();
29601             r.setStart(tgs[0],0);
29602             r.setEnd(tgs[0],0);
29603             this.getEditor().Selection.GetSelection().removeAllRanges();
29604             this.getEditor().Selection.GetSelection().addRange(r);
29605             this.getEditor().Focus();
29606         }
29607         
29608     },
29609     
29610     
29611     
29612     replaceTextarea : function()
29613     {
29614         if ( document.getElementById( this.getId() + '___Frame' ) )
29615             return ;
29616         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
29617         //{
29618             // We must check the elements firstly using the Id and then the name.
29619         var oTextarea = document.getElementById( this.getId() );
29620         
29621         var colElementsByName = document.getElementsByName( this.getId() ) ;
29622          
29623         oTextarea.style.display = 'none' ;
29624
29625         if ( oTextarea.tabIndex ) {            
29626             this.TabIndex = oTextarea.tabIndex ;
29627         }
29628         
29629         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
29630         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
29631         this.frame = Roo.get(this.getId() + '___Frame')
29632     },
29633     
29634     _getConfigHtml : function()
29635     {
29636         var sConfig = '' ;
29637
29638         for ( var o in this.fckconfig ) {
29639             sConfig += sConfig.length > 0  ? '&amp;' : '';
29640             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
29641         }
29642
29643         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
29644     },
29645     
29646     
29647     _getIFrameHtml : function()
29648     {
29649         var sFile = 'fckeditor.html' ;
29650         /* no idea what this is about..
29651         try
29652         {
29653             if ( (/fcksource=true/i).test( window.top.location.search ) )
29654                 sFile = 'fckeditor.original.html' ;
29655         }
29656         catch (e) { 
29657         */
29658
29659         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
29660         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
29661         
29662         
29663         var html = '<iframe id="' + this.getId() +
29664             '___Frame" src="' + sLink +
29665             '" width="' + this.width +
29666             '" height="' + this.height + '"' +
29667             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
29668             ' frameborder="0" scrolling="no"></iframe>' ;
29669
29670         return html ;
29671     },
29672     
29673     _insertHtmlBefore : function( html, element )
29674     {
29675         if ( element.insertAdjacentHTML )       {
29676             // IE
29677             element.insertAdjacentHTML( 'beforeBegin', html ) ;
29678         } else { // Gecko
29679             var oRange = document.createRange() ;
29680             oRange.setStartBefore( element ) ;
29681             var oFragment = oRange.createContextualFragment( html );
29682             element.parentNode.insertBefore( oFragment, element ) ;
29683         }
29684     }
29685     
29686     
29687   
29688     
29689     
29690     
29691     
29692
29693 });
29694
29695 //Roo.reg('fckeditor', Roo.form.FCKeditor);
29696
29697 function FCKeditor_OnComplete(editorInstance){
29698     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
29699     f.fckEditor = editorInstance;
29700     //console.log("loaded");
29701     f.fireEvent('editorinit', f, editorInstance);
29702
29703   
29704
29705  
29706
29707
29708
29709
29710
29711
29712
29713
29714
29715
29716
29717
29718
29719
29720
29721 //<script type="text/javascript">
29722 /**
29723  * @class Roo.form.GridField
29724  * @extends Roo.form.Field
29725  * Embed a grid (or editable grid into a form)
29726  * STATUS ALPHA
29727  * 
29728  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
29729  * it needs 
29730  * xgrid.store = Roo.data.Store
29731  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
29732  * xgrid.store.reader = Roo.data.JsonReader 
29733  * 
29734  * 
29735  * @constructor
29736  * Creates a new GridField
29737  * @param {Object} config Configuration options
29738  */
29739 Roo.form.GridField = function(config){
29740     Roo.form.GridField.superclass.constructor.call(this, config);
29741      
29742 };
29743
29744 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
29745     /**
29746      * @cfg {Number} width  - used to restrict width of grid..
29747      */
29748     width : 100,
29749     /**
29750      * @cfg {Number} height - used to restrict height of grid..
29751      */
29752     height : 50,
29753      /**
29754      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
29755          * 
29756          *}
29757      */
29758     xgrid : false, 
29759     /**
29760      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29761      * {tag: "input", type: "checkbox", autocomplete: "off"})
29762      */
29763    // defaultAutoCreate : { tag: 'div' },
29764     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29765     /**
29766      * @cfg {String} addTitle Text to include for adding a title.
29767      */
29768     addTitle : false,
29769     //
29770     onResize : function(){
29771         Roo.form.Field.superclass.onResize.apply(this, arguments);
29772     },
29773
29774     initEvents : function(){
29775         // Roo.form.Checkbox.superclass.initEvents.call(this);
29776         // has no events...
29777        
29778     },
29779
29780
29781     getResizeEl : function(){
29782         return this.wrap;
29783     },
29784
29785     getPositionEl : function(){
29786         return this.wrap;
29787     },
29788
29789     // private
29790     onRender : function(ct, position){
29791         
29792         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
29793         var style = this.style;
29794         delete this.style;
29795         
29796         Roo.form.GridField.superclass.onRender.call(this, ct, position);
29797         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
29798         this.viewEl = this.wrap.createChild({ tag: 'div' });
29799         if (style) {
29800             this.viewEl.applyStyles(style);
29801         }
29802         if (this.width) {
29803             this.viewEl.setWidth(this.width);
29804         }
29805         if (this.height) {
29806             this.viewEl.setHeight(this.height);
29807         }
29808         //if(this.inputValue !== undefined){
29809         //this.setValue(this.value);
29810         
29811         
29812         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
29813         
29814         
29815         this.grid.render();
29816         this.grid.getDataSource().on('remove', this.refreshValue, this);
29817         this.grid.getDataSource().on('update', this.refreshValue, this);
29818         this.grid.on('afteredit', this.refreshValue, this);
29819  
29820     },
29821      
29822     
29823     /**
29824      * Sets the value of the item. 
29825      * @param {String} either an object  or a string..
29826      */
29827     setValue : function(v){
29828         //this.value = v;
29829         v = v || []; // empty set..
29830         // this does not seem smart - it really only affects memoryproxy grids..
29831         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
29832             var ds = this.grid.getDataSource();
29833             // assumes a json reader..
29834             var data = {}
29835             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
29836             ds.loadData( data);
29837         }
29838         // clear selection so it does not get stale.
29839         if (this.grid.sm) { 
29840             this.grid.sm.clearSelections();
29841         }
29842         
29843         Roo.form.GridField.superclass.setValue.call(this, v);
29844         this.refreshValue();
29845         // should load data in the grid really....
29846     },
29847     
29848     // private
29849     refreshValue: function() {
29850          var val = [];
29851         this.grid.getDataSource().each(function(r) {
29852             val.push(r.data);
29853         });
29854         this.el.dom.value = Roo.encode(val);
29855     }
29856     
29857      
29858     
29859     
29860 });/*
29861  * Based on:
29862  * Ext JS Library 1.1.1
29863  * Copyright(c) 2006-2007, Ext JS, LLC.
29864  *
29865  * Originally Released Under LGPL - original licence link has changed is not relivant.
29866  *
29867  * Fork - LGPL
29868  * <script type="text/javascript">
29869  */
29870 /**
29871  * @class Roo.form.DisplayField
29872  * @extends Roo.form.Field
29873  * A generic Field to display non-editable data.
29874  * @constructor
29875  * Creates a new Display Field item.
29876  * @param {Object} config Configuration options
29877  */
29878 Roo.form.DisplayField = function(config){
29879     Roo.form.DisplayField.superclass.constructor.call(this, config);
29880     
29881 };
29882
29883 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
29884     inputType:      'hidden',
29885     allowBlank:     true,
29886     readOnly:         true,
29887     
29888  
29889     /**
29890      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29891      */
29892     focusClass : undefined,
29893     /**
29894      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29895      */
29896     fieldClass: 'x-form-field',
29897     
29898      /**
29899      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
29900      */
29901     valueRenderer: undefined,
29902     
29903     width: 100,
29904     /**
29905      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29906      * {tag: "input", type: "checkbox", autocomplete: "off"})
29907      */
29908      
29909  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29910
29911     onResize : function(){
29912         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
29913         
29914     },
29915
29916     initEvents : function(){
29917         // Roo.form.Checkbox.superclass.initEvents.call(this);
29918         // has no events...
29919        
29920     },
29921
29922
29923     getResizeEl : function(){
29924         return this.wrap;
29925     },
29926
29927     getPositionEl : function(){
29928         return this.wrap;
29929     },
29930
29931     // private
29932     onRender : function(ct, position){
29933         
29934         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
29935         //if(this.inputValue !== undefined){
29936         this.wrap = this.el.wrap();
29937         
29938         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
29939         
29940         if (this.bodyStyle) {
29941             this.viewEl.applyStyles(this.bodyStyle);
29942         }
29943         //this.viewEl.setStyle('padding', '2px');
29944         
29945         this.setValue(this.value);
29946         
29947     },
29948 /*
29949     // private
29950     initValue : Roo.emptyFn,
29951
29952   */
29953
29954         // private
29955     onClick : function(){
29956         
29957     },
29958
29959     /**
29960      * Sets the checked state of the checkbox.
29961      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
29962      */
29963     setValue : function(v){
29964         this.value = v;
29965         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
29966         // this might be called before we have a dom element..
29967         if (!this.viewEl) {
29968             return;
29969         }
29970         this.viewEl.dom.innerHTML = html;
29971         Roo.form.DisplayField.superclass.setValue.call(this, v);
29972
29973     }
29974 });/*
29975  * 
29976  * Licence- LGPL
29977  * 
29978  */
29979
29980 /**
29981  * @class Roo.form.DayPicker
29982  * @extends Roo.form.Field
29983  * A Day picker show [M] [T] [W] ....
29984  * @constructor
29985  * Creates a new Day Picker
29986  * @param {Object} config Configuration options
29987  */
29988 Roo.form.DayPicker= function(config){
29989     Roo.form.DayPicker.superclass.constructor.call(this, config);
29990      
29991 };
29992
29993 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
29994     /**
29995      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29996      */
29997     focusClass : undefined,
29998     /**
29999      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30000      */
30001     fieldClass: "x-form-field",
30002    
30003     /**
30004      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30005      * {tag: "input", type: "checkbox", autocomplete: "off"})
30006      */
30007     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30008     
30009    
30010     actionMode : 'viewEl', 
30011     //
30012     // private
30013  
30014     inputType : 'hidden',
30015     
30016      
30017     inputElement: false, // real input element?
30018     basedOn: false, // ????
30019     
30020     isFormField: true, // not sure where this is needed!!!!
30021
30022     onResize : function(){
30023         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30024         if(!this.boxLabel){
30025             this.el.alignTo(this.wrap, 'c-c');
30026         }
30027     },
30028
30029     initEvents : function(){
30030         Roo.form.Checkbox.superclass.initEvents.call(this);
30031         this.el.on("click", this.onClick,  this);
30032         this.el.on("change", this.onClick,  this);
30033     },
30034
30035
30036     getResizeEl : function(){
30037         return this.wrap;
30038     },
30039
30040     getPositionEl : function(){
30041         return this.wrap;
30042     },
30043
30044     
30045     // private
30046     onRender : function(ct, position){
30047         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30048        
30049         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30050         
30051         var r1 = '<table><tr>';
30052         var r2 = '<tr class="x-form-daypick-icons">';
30053         for (var i=0; i < 7; i++) {
30054             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30055             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30056         }
30057         
30058         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30059         viewEl.select('img').on('click', this.onClick, this);
30060         this.viewEl = viewEl;   
30061         
30062         
30063         // this will not work on Chrome!!!
30064         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30065         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30066         
30067         
30068           
30069
30070     },
30071
30072     // private
30073     initValue : Roo.emptyFn,
30074
30075     /**
30076      * Returns the checked state of the checkbox.
30077      * @return {Boolean} True if checked, else false
30078      */
30079     getValue : function(){
30080         return this.el.dom.value;
30081         
30082     },
30083
30084         // private
30085     onClick : function(e){ 
30086         //this.setChecked(!this.checked);
30087         Roo.get(e.target).toggleClass('x-menu-item-checked');
30088         this.refreshValue();
30089         //if(this.el.dom.checked != this.checked){
30090         //    this.setValue(this.el.dom.checked);
30091        // }
30092     },
30093     
30094     // private
30095     refreshValue : function()
30096     {
30097         var val = '';
30098         this.viewEl.select('img',true).each(function(e,i,n)  {
30099             val += e.is(".x-menu-item-checked") ? String(n) : '';
30100         });
30101         this.setValue(val, true);
30102     },
30103
30104     /**
30105      * Sets the checked state of the checkbox.
30106      * On is always based on a string comparison between inputValue and the param.
30107      * @param {Boolean/String} value - the value to set 
30108      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30109      */
30110     setValue : function(v,suppressEvent){
30111         if (!this.el.dom) {
30112             return;
30113         }
30114         var old = this.el.dom.value ;
30115         this.el.dom.value = v;
30116         if (suppressEvent) {
30117             return ;
30118         }
30119          
30120         // update display..
30121         this.viewEl.select('img',true).each(function(e,i,n)  {
30122             
30123             var on = e.is(".x-menu-item-checked");
30124             var newv = v.indexOf(String(n)) > -1;
30125             if (on != newv) {
30126                 e.toggleClass('x-menu-item-checked');
30127             }
30128             
30129         });
30130         
30131         
30132         this.fireEvent('change', this, v, old);
30133         
30134         
30135     },
30136    
30137     // handle setting of hidden value by some other method!!?!?
30138     setFromHidden: function()
30139     {
30140         if(!this.el){
30141             return;
30142         }
30143         //console.log("SET FROM HIDDEN");
30144         //alert('setFrom hidden');
30145         this.setValue(this.el.dom.value);
30146     },
30147     
30148     onDestroy : function()
30149     {
30150         if(this.viewEl){
30151             Roo.get(this.viewEl).remove();
30152         }
30153          
30154         Roo.form.DayPicker.superclass.onDestroy.call(this);
30155     }
30156
30157 });/*
30158  * RooJS Library 1.1.1
30159  * Copyright(c) 2008-2011  Alan Knowles
30160  *
30161  * License - LGPL
30162  */
30163  
30164
30165 /**
30166  * @class Roo.form.ComboCheck
30167  * @extends Roo.form.ComboBox
30168  * A combobox for multiple select items.
30169  *
30170  * FIXME - could do with a reset button..
30171  * 
30172  * @constructor
30173  * Create a new ComboCheck
30174  * @param {Object} config Configuration options
30175  */
30176 Roo.form.ComboCheck = function(config){
30177     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30178     // should verify some data...
30179     // like
30180     // hiddenName = required..
30181     // displayField = required
30182     // valudField == required
30183     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30184     var _t = this;
30185     Roo.each(req, function(e) {
30186         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30187             throw "Roo.form.ComboCheck : missing value for: " + e;
30188         }
30189     });
30190     
30191     
30192 };
30193
30194 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30195      
30196      
30197     editable : false,
30198      
30199     selectedClass: 'x-menu-item-checked', 
30200     
30201     // private
30202     onRender : function(ct, position){
30203         var _t = this;
30204         
30205         
30206         
30207         if(!this.tpl){
30208             var cls = 'x-combo-list';
30209
30210             
30211             this.tpl =  new Roo.Template({
30212                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30213                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30214                    '<span>{' + this.displayField + '}</span>' +
30215                     '</div>' 
30216                 
30217             });
30218         }
30219  
30220         
30221         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30222         this.view.singleSelect = false;
30223         this.view.multiSelect = true;
30224         this.view.toggleSelect = true;
30225         this.pageTb.add(new Roo.Toolbar.Fill(), {
30226             
30227             text: 'Done',
30228             handler: function()
30229             {
30230                 _t.collapse();
30231             }
30232         });
30233     },
30234     
30235     onViewOver : function(e, t){
30236         // do nothing...
30237         return;
30238         
30239     },
30240     
30241     onViewClick : function(doFocus,index){
30242         return;
30243         
30244     },
30245     select: function () {
30246         //Roo.log("SELECT CALLED");
30247     },
30248      
30249     selectByValue : function(xv, scrollIntoView){
30250         var ar = this.getValueArray();
30251         var sels = [];
30252         
30253         Roo.each(ar, function(v) {
30254             if(v === undefined || v === null){
30255                 return;
30256             }
30257             var r = this.findRecord(this.valueField, v);
30258             if(r){
30259                 sels.push(this.store.indexOf(r))
30260                 
30261             }
30262         },this);
30263         this.view.select(sels);
30264         return false;
30265     },
30266     
30267     
30268     
30269     onSelect : function(record, index){
30270        // Roo.log("onselect Called");
30271        // this is only called by the clear button now..
30272         this.view.clearSelections();
30273         this.setValue('[]');
30274         if (this.value != this.valueBefore) {
30275             this.fireEvent('change', this, this.value, this.valueBefore);
30276         }
30277     },
30278     getValueArray : function()
30279     {
30280         var ar = [] ;
30281         
30282         try {
30283             //Roo.log(this.value);
30284             if (typeof(this.value) == 'undefined') {
30285                 return [];
30286             }
30287             var ar = Roo.decode(this.value);
30288             return  ar instanceof Array ? ar : []; //?? valid?
30289             
30290         } catch(e) {
30291             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30292             return [];
30293         }
30294          
30295     },
30296     expand : function ()
30297     {
30298         Roo.form.ComboCheck.superclass.expand.call(this);
30299         this.valueBefore = this.value;
30300         
30301
30302     },
30303     
30304     collapse : function(){
30305         Roo.form.ComboCheck.superclass.collapse.call(this);
30306         var sl = this.view.getSelectedIndexes();
30307         var st = this.store;
30308         var nv = [];
30309         var tv = [];
30310         var r;
30311         Roo.each(sl, function(i) {
30312             r = st.getAt(i);
30313             nv.push(r.get(this.valueField));
30314         },this);
30315         this.setValue(Roo.encode(nv));
30316         if (this.value != this.valueBefore) {
30317
30318             this.fireEvent('change', this, this.value, this.valueBefore);
30319         }
30320         
30321     },
30322     
30323     setValue : function(v){
30324         // Roo.log(v);
30325         this.value = v;
30326         
30327         var vals = this.getValueArray();
30328         var tv = [];
30329         Roo.each(vals, function(k) {
30330             var r = this.findRecord(this.valueField, k);
30331             if(r){
30332                 tv.push(r.data[this.displayField]);
30333             }else if(this.valueNotFoundText !== undefined){
30334                 tv.push( this.valueNotFoundText );
30335             }
30336         },this);
30337        // Roo.log(tv);
30338         
30339         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30340         this.hiddenField.value = v;
30341         this.value = v;
30342     }
30343     
30344 });//<script type="text/javasscript">
30345  
30346
30347 /**
30348  * @class Roo.DDView
30349  * A DnD enabled version of Roo.View.
30350  * @param {Element/String} container The Element in which to create the View.
30351  * @param {String} tpl The template string used to create the markup for each element of the View
30352  * @param {Object} config The configuration properties. These include all the config options of
30353  * {@link Roo.View} plus some specific to this class.<br>
30354  * <p>
30355  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
30356  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
30357  * <p>
30358  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
30359 .x-view-drag-insert-above {
30360         border-top:1px dotted #3366cc;
30361 }
30362 .x-view-drag-insert-below {
30363         border-bottom:1px dotted #3366cc;
30364 }
30365 </code></pre>
30366  * 
30367  */
30368  
30369 Roo.DDView = function(container, tpl, config) {
30370     Roo.DDView.superclass.constructor.apply(this, arguments);
30371     this.getEl().setStyle("outline", "0px none");
30372     this.getEl().unselectable();
30373     if (this.dragGroup) {
30374                 this.setDraggable(this.dragGroup.split(","));
30375     }
30376     if (this.dropGroup) {
30377                 this.setDroppable(this.dropGroup.split(","));
30378     }
30379     if (this.deletable) {
30380         this.setDeletable();
30381     }
30382     this.isDirtyFlag = false;
30383         this.addEvents({
30384                 "drop" : true
30385         });
30386 };
30387
30388 Roo.extend(Roo.DDView, Roo.View, {
30389 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
30390 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
30391 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
30392 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
30393
30394         isFormField: true,
30395
30396         reset: Roo.emptyFn,
30397         
30398         clearInvalid: Roo.form.Field.prototype.clearInvalid,
30399
30400         validate: function() {
30401                 return true;
30402         },
30403         
30404         destroy: function() {
30405                 this.purgeListeners();
30406                 this.getEl.removeAllListeners();
30407                 this.getEl().remove();
30408                 if (this.dragZone) {
30409                         if (this.dragZone.destroy) {
30410                                 this.dragZone.destroy();
30411                         }
30412                 }
30413                 if (this.dropZone) {
30414                         if (this.dropZone.destroy) {
30415                                 this.dropZone.destroy();
30416                         }
30417                 }
30418         },
30419
30420 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
30421         getName: function() {
30422                 return this.name;
30423         },
30424
30425 /**     Loads the View from a JSON string representing the Records to put into the Store. */
30426         setValue: function(v) {
30427                 if (!this.store) {
30428                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
30429                 }
30430                 var data = {};
30431                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
30432                 this.store.proxy = new Roo.data.MemoryProxy(data);
30433                 this.store.load();
30434         },
30435
30436 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
30437         getValue: function() {
30438                 var result = '(';
30439                 this.store.each(function(rec) {
30440                         result += rec.id + ',';
30441                 });
30442                 return result.substr(0, result.length - 1) + ')';
30443         },
30444         
30445         getIds: function() {
30446                 var i = 0, result = new Array(this.store.getCount());
30447                 this.store.each(function(rec) {
30448                         result[i++] = rec.id;
30449                 });
30450                 return result;
30451         },
30452         
30453         isDirty: function() {
30454                 return this.isDirtyFlag;
30455         },
30456
30457 /**
30458  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
30459  *      whole Element becomes the target, and this causes the drop gesture to append.
30460  */
30461     getTargetFromEvent : function(e) {
30462                 var target = e.getTarget();
30463                 while ((target !== null) && (target.parentNode != this.el.dom)) {
30464                 target = target.parentNode;
30465                 }
30466                 if (!target) {
30467                         target = this.el.dom.lastChild || this.el.dom;
30468                 }
30469                 return target;
30470     },
30471
30472 /**
30473  *      Create the drag data which consists of an object which has the property "ddel" as
30474  *      the drag proxy element. 
30475  */
30476     getDragData : function(e) {
30477         var target = this.findItemFromChild(e.getTarget());
30478                 if(target) {
30479                         this.handleSelection(e);
30480                         var selNodes = this.getSelectedNodes();
30481             var dragData = {
30482                 source: this,
30483                 copy: this.copy || (this.allowCopy && e.ctrlKey),
30484                 nodes: selNodes,
30485                 records: []
30486                         };
30487                         var selectedIndices = this.getSelectedIndexes();
30488                         for (var i = 0; i < selectedIndices.length; i++) {
30489                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
30490                         }
30491                         if (selNodes.length == 1) {
30492                                 dragData.ddel = target.cloneNode(true); // the div element
30493                         } else {
30494                                 var div = document.createElement('div'); // create the multi element drag "ghost"
30495                                 div.className = 'multi-proxy';
30496                                 for (var i = 0, len = selNodes.length; i < len; i++) {
30497                                         div.appendChild(selNodes[i].cloneNode(true));
30498                                 }
30499                                 dragData.ddel = div;
30500                         }
30501             //console.log(dragData)
30502             //console.log(dragData.ddel.innerHTML)
30503                         return dragData;
30504                 }
30505         //console.log('nodragData')
30506                 return false;
30507     },
30508     
30509 /**     Specify to which ddGroup items in this DDView may be dragged. */
30510     setDraggable: function(ddGroup) {
30511         if (ddGroup instanceof Array) {
30512                 Roo.each(ddGroup, this.setDraggable, this);
30513                 return;
30514         }
30515         if (this.dragZone) {
30516                 this.dragZone.addToGroup(ddGroup);
30517         } else {
30518                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
30519                                 containerScroll: true,
30520                                 ddGroup: ddGroup 
30521
30522                         });
30523 //                      Draggability implies selection. DragZone's mousedown selects the element.
30524                         if (!this.multiSelect) { this.singleSelect = true; }
30525
30526 //                      Wire the DragZone's handlers up to methods in *this*
30527                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
30528                 }
30529     },
30530
30531 /**     Specify from which ddGroup this DDView accepts drops. */
30532     setDroppable: function(ddGroup) {
30533         if (ddGroup instanceof Array) {
30534                 Roo.each(ddGroup, this.setDroppable, this);
30535                 return;
30536         }
30537         if (this.dropZone) {
30538                 this.dropZone.addToGroup(ddGroup);
30539         } else {
30540                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
30541                                 containerScroll: true,
30542                                 ddGroup: ddGroup
30543                         });
30544
30545 //                      Wire the DropZone's handlers up to methods in *this*
30546                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
30547                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
30548                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
30549                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
30550                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
30551                 }
30552     },
30553
30554 /**     Decide whether to drop above or below a View node. */
30555     getDropPoint : function(e, n, dd){
30556         if (n == this.el.dom) { return "above"; }
30557                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
30558                 var c = t + (b - t) / 2;
30559                 var y = Roo.lib.Event.getPageY(e);
30560                 if(y <= c) {
30561                         return "above";
30562                 }else{
30563                         return "below";
30564                 }
30565     },
30566
30567     onNodeEnter : function(n, dd, e, data){
30568                 return false;
30569     },
30570     
30571     onNodeOver : function(n, dd, e, data){
30572                 var pt = this.getDropPoint(e, n, dd);
30573                 // set the insert point style on the target node
30574                 var dragElClass = this.dropNotAllowed;
30575                 if (pt) {
30576                         var targetElClass;
30577                         if (pt == "above"){
30578                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
30579                                 targetElClass = "x-view-drag-insert-above";
30580                         } else {
30581                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
30582                                 targetElClass = "x-view-drag-insert-below";
30583                         }
30584                         if (this.lastInsertClass != targetElClass){
30585                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
30586                                 this.lastInsertClass = targetElClass;
30587                         }
30588                 }
30589                 return dragElClass;
30590         },
30591
30592     onNodeOut : function(n, dd, e, data){
30593                 this.removeDropIndicators(n);
30594     },
30595
30596     onNodeDrop : function(n, dd, e, data){
30597         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
30598                 return false;
30599         }
30600         var pt = this.getDropPoint(e, n, dd);
30601                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
30602                 if (pt == "below") { insertAt++; }
30603                 for (var i = 0; i < data.records.length; i++) {
30604                         var r = data.records[i];
30605                         var dup = this.store.getById(r.id);
30606                         if (dup && (dd != this.dragZone)) {
30607                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
30608                         } else {
30609                                 if (data.copy) {
30610                                         this.store.insert(insertAt++, r.copy());
30611                                 } else {
30612                                         data.source.isDirtyFlag = true;
30613                                         r.store.remove(r);
30614                                         this.store.insert(insertAt++, r);
30615                                 }
30616                                 this.isDirtyFlag = true;
30617                         }
30618                 }
30619                 this.dragZone.cachedTarget = null;
30620                 return true;
30621     },
30622
30623     removeDropIndicators : function(n){
30624                 if(n){
30625                         Roo.fly(n).removeClass([
30626                                 "x-view-drag-insert-above",
30627                                 "x-view-drag-insert-below"]);
30628                         this.lastInsertClass = "_noclass";
30629                 }
30630     },
30631
30632 /**
30633  *      Utility method. Add a delete option to the DDView's context menu.
30634  *      @param {String} imageUrl The URL of the "delete" icon image.
30635  */
30636         setDeletable: function(imageUrl) {
30637                 if (!this.singleSelect && !this.multiSelect) {
30638                         this.singleSelect = true;
30639                 }
30640                 var c = this.getContextMenu();
30641                 this.contextMenu.on("itemclick", function(item) {
30642                         switch (item.id) {
30643                                 case "delete":
30644                                         this.remove(this.getSelectedIndexes());
30645                                         break;
30646                         }
30647                 }, this);
30648                 this.contextMenu.add({
30649                         icon: imageUrl,
30650                         id: "delete",
30651                         text: 'Delete'
30652                 });
30653         },
30654         
30655 /**     Return the context menu for this DDView. */
30656         getContextMenu: function() {
30657                 if (!this.contextMenu) {
30658 //                      Create the View's context menu
30659                         this.contextMenu = new Roo.menu.Menu({
30660                                 id: this.id + "-contextmenu"
30661                         });
30662                         this.el.on("contextmenu", this.showContextMenu, this);
30663                 }
30664                 return this.contextMenu;
30665         },
30666         
30667         disableContextMenu: function() {
30668                 if (this.contextMenu) {
30669                         this.el.un("contextmenu", this.showContextMenu, this);
30670                 }
30671         },
30672
30673         showContextMenu: function(e, item) {
30674         item = this.findItemFromChild(e.getTarget());
30675                 if (item) {
30676                         e.stopEvent();
30677                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
30678                         this.contextMenu.showAt(e.getXY());
30679             }
30680     },
30681
30682 /**
30683  *      Remove {@link Roo.data.Record}s at the specified indices.
30684  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
30685  */
30686     remove: function(selectedIndices) {
30687                 selectedIndices = [].concat(selectedIndices);
30688                 for (var i = 0; i < selectedIndices.length; i++) {
30689                         var rec = this.store.getAt(selectedIndices[i]);
30690                         this.store.remove(rec);
30691                 }
30692     },
30693
30694 /**
30695  *      Double click fires the event, but also, if this is draggable, and there is only one other
30696  *      related DropZone, it transfers the selected node.
30697  */
30698     onDblClick : function(e){
30699         var item = this.findItemFromChild(e.getTarget());
30700         if(item){
30701             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
30702                 return false;
30703             }
30704             if (this.dragGroup) {
30705                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
30706                     while (targets.indexOf(this.dropZone) > -1) {
30707                             targets.remove(this.dropZone);
30708                                 }
30709                     if (targets.length == 1) {
30710                                         this.dragZone.cachedTarget = null;
30711                         var el = Roo.get(targets[0].getEl());
30712                         var box = el.getBox(true);
30713                         targets[0].onNodeDrop(el.dom, {
30714                                 target: el.dom,
30715                                 xy: [box.x, box.y + box.height - 1]
30716                         }, null, this.getDragData(e));
30717                     }
30718                 }
30719         }
30720     },
30721     
30722     handleSelection: function(e) {
30723                 this.dragZone.cachedTarget = null;
30724         var item = this.findItemFromChild(e.getTarget());
30725         if (!item) {
30726                 this.clearSelections(true);
30727                 return;
30728         }
30729                 if (item && (this.multiSelect || this.singleSelect)){
30730                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
30731                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
30732                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
30733                                 this.unselect(item);
30734                         } else {
30735                                 this.select(item, this.multiSelect && e.ctrlKey);
30736                                 this.lastSelection = item;
30737                         }
30738                 }
30739     },
30740
30741     onItemClick : function(item, index, e){
30742                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
30743                         return false;
30744                 }
30745                 return true;
30746     },
30747
30748     unselect : function(nodeInfo, suppressEvent){
30749                 var node = this.getNode(nodeInfo);
30750                 if(node && this.isSelected(node)){
30751                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
30752                                 Roo.fly(node).removeClass(this.selectedClass);
30753                                 this.selections.remove(node);
30754                                 if(!suppressEvent){
30755                                         this.fireEvent("selectionchange", this, this.selections);
30756                                 }
30757                         }
30758                 }
30759     }
30760 });
30761 /*
30762  * Based on:
30763  * Ext JS Library 1.1.1
30764  * Copyright(c) 2006-2007, Ext JS, LLC.
30765  *
30766  * Originally Released Under LGPL - original licence link has changed is not relivant.
30767  *
30768  * Fork - LGPL
30769  * <script type="text/javascript">
30770  */
30771  
30772 /**
30773  * @class Roo.LayoutManager
30774  * @extends Roo.util.Observable
30775  * Base class for layout managers.
30776  */
30777 Roo.LayoutManager = function(container, config){
30778     Roo.LayoutManager.superclass.constructor.call(this);
30779     this.el = Roo.get(container);
30780     // ie scrollbar fix
30781     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
30782         document.body.scroll = "no";
30783     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
30784         this.el.position('relative');
30785     }
30786     this.id = this.el.id;
30787     this.el.addClass("x-layout-container");
30788     /** false to disable window resize monitoring @type Boolean */
30789     this.monitorWindowResize = true;
30790     this.regions = {};
30791     this.addEvents({
30792         /**
30793          * @event layout
30794          * Fires when a layout is performed. 
30795          * @param {Roo.LayoutManager} this
30796          */
30797         "layout" : true,
30798         /**
30799          * @event regionresized
30800          * Fires when the user resizes a region. 
30801          * @param {Roo.LayoutRegion} region The resized region
30802          * @param {Number} newSize The new size (width for east/west, height for north/south)
30803          */
30804         "regionresized" : true,
30805         /**
30806          * @event regioncollapsed
30807          * Fires when a region is collapsed. 
30808          * @param {Roo.LayoutRegion} region The collapsed region
30809          */
30810         "regioncollapsed" : true,
30811         /**
30812          * @event regionexpanded
30813          * Fires when a region is expanded.  
30814          * @param {Roo.LayoutRegion} region The expanded region
30815          */
30816         "regionexpanded" : true
30817     });
30818     this.updating = false;
30819     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
30820 };
30821
30822 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
30823     /**
30824      * Returns true if this layout is currently being updated
30825      * @return {Boolean}
30826      */
30827     isUpdating : function(){
30828         return this.updating; 
30829     },
30830     
30831     /**
30832      * Suspend the LayoutManager from doing auto-layouts while
30833      * making multiple add or remove calls
30834      */
30835     beginUpdate : function(){
30836         this.updating = true;    
30837     },
30838     
30839     /**
30840      * Restore auto-layouts and optionally disable the manager from performing a layout
30841      * @param {Boolean} noLayout true to disable a layout update 
30842      */
30843     endUpdate : function(noLayout){
30844         this.updating = false;
30845         if(!noLayout){
30846             this.layout();
30847         }    
30848     },
30849     
30850     layout: function(){
30851         
30852     },
30853     
30854     onRegionResized : function(region, newSize){
30855         this.fireEvent("regionresized", region, newSize);
30856         this.layout();
30857     },
30858     
30859     onRegionCollapsed : function(region){
30860         this.fireEvent("regioncollapsed", region);
30861     },
30862     
30863     onRegionExpanded : function(region){
30864         this.fireEvent("regionexpanded", region);
30865     },
30866         
30867     /**
30868      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
30869      * performs box-model adjustments.
30870      * @return {Object} The size as an object {width: (the width), height: (the height)}
30871      */
30872     getViewSize : function(){
30873         var size;
30874         if(this.el.dom != document.body){
30875             size = this.el.getSize();
30876         }else{
30877             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
30878         }
30879         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
30880         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30881         return size;
30882     },
30883     
30884     /**
30885      * Returns the Element this layout is bound to.
30886      * @return {Roo.Element}
30887      */
30888     getEl : function(){
30889         return this.el;
30890     },
30891     
30892     /**
30893      * Returns the specified region.
30894      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
30895      * @return {Roo.LayoutRegion}
30896      */
30897     getRegion : function(target){
30898         return this.regions[target.toLowerCase()];
30899     },
30900     
30901     onWindowResize : function(){
30902         if(this.monitorWindowResize){
30903             this.layout();
30904         }
30905     }
30906 });/*
30907  * Based on:
30908  * Ext JS Library 1.1.1
30909  * Copyright(c) 2006-2007, Ext JS, LLC.
30910  *
30911  * Originally Released Under LGPL - original licence link has changed is not relivant.
30912  *
30913  * Fork - LGPL
30914  * <script type="text/javascript">
30915  */
30916 /**
30917  * @class Roo.BorderLayout
30918  * @extends Roo.LayoutManager
30919  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
30920  * please see: <br><br>
30921  * <a href="http://www.jackslocum.com/yui/2006/10/19/cross-browser-web-20-layouts-with-yahoo-ui/">Cross Browser Layouts - Part 1</a><br>
30922  * <a href="http://www.jackslocum.com/yui/2006/10/28/cross-browser-web-20-layouts-part-2-ajax-feed-viewer-20/">Cross Browser Layouts - Part 2</a><br><br>
30923  * Example:
30924  <pre><code>
30925  var layout = new Roo.BorderLayout(document.body, {
30926     north: {
30927         initialSize: 25,
30928         titlebar: false
30929     },
30930     west: {
30931         split:true,
30932         initialSize: 200,
30933         minSize: 175,
30934         maxSize: 400,
30935         titlebar: true,
30936         collapsible: true
30937     },
30938     east: {
30939         split:true,
30940         initialSize: 202,
30941         minSize: 175,
30942         maxSize: 400,
30943         titlebar: true,
30944         collapsible: true
30945     },
30946     south: {
30947         split:true,
30948         initialSize: 100,
30949         minSize: 100,
30950         maxSize: 200,
30951         titlebar: true,
30952         collapsible: true
30953     },
30954     center: {
30955         titlebar: true,
30956         autoScroll:true,
30957         resizeTabs: true,
30958         minTabWidth: 50,
30959         preferredTabWidth: 150
30960     }
30961 });
30962
30963 // shorthand
30964 var CP = Roo.ContentPanel;
30965
30966 layout.beginUpdate();
30967 layout.add("north", new CP("north", "North"));
30968 layout.add("south", new CP("south", {title: "South", closable: true}));
30969 layout.add("west", new CP("west", {title: "West"}));
30970 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
30971 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
30972 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
30973 layout.getRegion("center").showPanel("center1");
30974 layout.endUpdate();
30975 </code></pre>
30976
30977 <b>The container the layout is rendered into can be either the body element or any other element.
30978 If it is not the body element, the container needs to either be an absolute positioned element,
30979 or you will need to add "position:relative" to the css of the container.  You will also need to specify
30980 the container size if it is not the body element.</b>
30981
30982 * @constructor
30983 * Create a new BorderLayout
30984 * @param {String/HTMLElement/Element} container The container this layout is bound to
30985 * @param {Object} config Configuration options
30986  */
30987 Roo.BorderLayout = function(container, config){
30988     config = config || {};
30989     Roo.BorderLayout.superclass.constructor.call(this, container, config);
30990     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
30991     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
30992         var target = this.factory.validRegions[i];
30993         if(config[target]){
30994             this.addRegion(target, config[target]);
30995         }
30996     }
30997 };
30998
30999 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
31000     /**
31001      * Creates and adds a new region if it doesn't already exist.
31002      * @param {String} target The target region key (north, south, east, west or center).
31003      * @param {Object} config The regions config object
31004      * @return {BorderLayoutRegion} The new region
31005      */
31006     addRegion : function(target, config){
31007         if(!this.regions[target]){
31008             var r = this.factory.create(target, this, config);
31009             this.bindRegion(target, r);
31010         }
31011         return this.regions[target];
31012     },
31013
31014     // private (kinda)
31015     bindRegion : function(name, r){
31016         this.regions[name] = r;
31017         r.on("visibilitychange", this.layout, this);
31018         r.on("paneladded", this.layout, this);
31019         r.on("panelremoved", this.layout, this);
31020         r.on("invalidated", this.layout, this);
31021         r.on("resized", this.onRegionResized, this);
31022         r.on("collapsed", this.onRegionCollapsed, this);
31023         r.on("expanded", this.onRegionExpanded, this);
31024     },
31025
31026     /**
31027      * Performs a layout update.
31028      */
31029     layout : function(){
31030         if(this.updating) return;
31031         var size = this.getViewSize();
31032         var w = size.width;
31033         var h = size.height;
31034         var centerW = w;
31035         var centerH = h;
31036         var centerY = 0;
31037         var centerX = 0;
31038         //var x = 0, y = 0;
31039
31040         var rs = this.regions;
31041         var north = rs["north"];
31042         var south = rs["south"]; 
31043         var west = rs["west"];
31044         var east = rs["east"];
31045         var center = rs["center"];
31046         //if(this.hideOnLayout){ // not supported anymore
31047             //c.el.setStyle("display", "none");
31048         //}
31049         if(north && north.isVisible()){
31050             var b = north.getBox();
31051             var m = north.getMargins();
31052             b.width = w - (m.left+m.right);
31053             b.x = m.left;
31054             b.y = m.top;
31055             centerY = b.height + b.y + m.bottom;
31056             centerH -= centerY;
31057             north.updateBox(this.safeBox(b));
31058         }
31059         if(south && south.isVisible()){
31060             var b = south.getBox();
31061             var m = south.getMargins();
31062             b.width = w - (m.left+m.right);
31063             b.x = m.left;
31064             var totalHeight = (b.height + m.top + m.bottom);
31065             b.y = h - totalHeight + m.top;
31066             centerH -= totalHeight;
31067             south.updateBox(this.safeBox(b));
31068         }
31069         if(west && west.isVisible()){
31070             var b = west.getBox();
31071             var m = west.getMargins();
31072             b.height = centerH - (m.top+m.bottom);
31073             b.x = m.left;
31074             b.y = centerY + m.top;
31075             var totalWidth = (b.width + m.left + m.right);
31076             centerX += totalWidth;
31077             centerW -= totalWidth;
31078             west.updateBox(this.safeBox(b));
31079         }
31080         if(east && east.isVisible()){
31081             var b = east.getBox();
31082             var m = east.getMargins();
31083             b.height = centerH - (m.top+m.bottom);
31084             var totalWidth = (b.width + m.left + m.right);
31085             b.x = w - totalWidth + m.left;
31086             b.y = centerY + m.top;
31087             centerW -= totalWidth;
31088             east.updateBox(this.safeBox(b));
31089         }
31090         if(center){
31091             var m = center.getMargins();
31092             var centerBox = {
31093                 x: centerX + m.left,
31094                 y: centerY + m.top,
31095                 width: centerW - (m.left+m.right),
31096                 height: centerH - (m.top+m.bottom)
31097             };
31098             //if(this.hideOnLayout){
31099                 //center.el.setStyle("display", "block");
31100             //}
31101             center.updateBox(this.safeBox(centerBox));
31102         }
31103         this.el.repaint();
31104         this.fireEvent("layout", this);
31105     },
31106
31107     // private
31108     safeBox : function(box){
31109         box.width = Math.max(0, box.width);
31110         box.height = Math.max(0, box.height);
31111         return box;
31112     },
31113
31114     /**
31115      * Adds a ContentPanel (or subclass) to this layout.
31116      * @param {String} target The target region key (north, south, east, west or center).
31117      * @param {Roo.ContentPanel} panel The panel to add
31118      * @return {Roo.ContentPanel} The added panel
31119      */
31120     add : function(target, panel){
31121          
31122         target = target.toLowerCase();
31123         return this.regions[target].add(panel);
31124     },
31125
31126     /**
31127      * Remove a ContentPanel (or subclass) to this layout.
31128      * @param {String} target The target region key (north, south, east, west or center).
31129      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31130      * @return {Roo.ContentPanel} The removed panel
31131      */
31132     remove : function(target, panel){
31133         target = target.toLowerCase();
31134         return this.regions[target].remove(panel);
31135     },
31136
31137     /**
31138      * Searches all regions for a panel with the specified id
31139      * @param {String} panelId
31140      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31141      */
31142     findPanel : function(panelId){
31143         var rs = this.regions;
31144         for(var target in rs){
31145             if(typeof rs[target] != "function"){
31146                 var p = rs[target].getPanel(panelId);
31147                 if(p){
31148                     return p;
31149                 }
31150             }
31151         }
31152         return null;
31153     },
31154
31155     /**
31156      * Searches all regions for a panel with the specified id and activates (shows) it.
31157      * @param {String/ContentPanel} panelId The panels id or the panel itself
31158      * @return {Roo.ContentPanel} The shown panel or null
31159      */
31160     showPanel : function(panelId) {
31161       var rs = this.regions;
31162       for(var target in rs){
31163          var r = rs[target];
31164          if(typeof r != "function"){
31165             if(r.hasPanel(panelId)){
31166                return r.showPanel(panelId);
31167             }
31168          }
31169       }
31170       return null;
31171    },
31172
31173    /**
31174      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31175      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31176      */
31177     restoreState : function(provider){
31178         if(!provider){
31179             provider = Roo.state.Manager;
31180         }
31181         var sm = new Roo.LayoutStateManager();
31182         sm.init(this, provider);
31183     },
31184
31185     /**
31186      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
31187      * object should contain properties for each region to add ContentPanels to, and each property's value should be
31188      * a valid ContentPanel config object.  Example:
31189      * <pre><code>
31190 // Create the main layout
31191 var layout = new Roo.BorderLayout('main-ct', {
31192     west: {
31193         split:true,
31194         minSize: 175,
31195         titlebar: true
31196     },
31197     center: {
31198         title:'Components'
31199     }
31200 }, 'main-ct');
31201
31202 // Create and add multiple ContentPanels at once via configs
31203 layout.batchAdd({
31204    west: {
31205        id: 'source-files',
31206        autoCreate:true,
31207        title:'Ext Source Files',
31208        autoScroll:true,
31209        fitToFrame:true
31210    },
31211    center : {
31212        el: cview,
31213        autoScroll:true,
31214        fitToFrame:true,
31215        toolbar: tb,
31216        resizeEl:'cbody'
31217    }
31218 });
31219 </code></pre>
31220      * @param {Object} regions An object containing ContentPanel configs by region name
31221      */
31222     batchAdd : function(regions){
31223         this.beginUpdate();
31224         for(var rname in regions){
31225             var lr = this.regions[rname];
31226             if(lr){
31227                 this.addTypedPanels(lr, regions[rname]);
31228             }
31229         }
31230         this.endUpdate();
31231     },
31232
31233     // private
31234     addTypedPanels : function(lr, ps){
31235         if(typeof ps == 'string'){
31236             lr.add(new Roo.ContentPanel(ps));
31237         }
31238         else if(ps instanceof Array){
31239             for(var i =0, len = ps.length; i < len; i++){
31240                 this.addTypedPanels(lr, ps[i]);
31241             }
31242         }
31243         else if(!ps.events){ // raw config?
31244             var el = ps.el;
31245             delete ps.el; // prevent conflict
31246             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
31247         }
31248         else {  // panel object assumed!
31249             lr.add(ps);
31250         }
31251     },
31252     /**
31253      * Adds a xtype elements to the layout.
31254      * <pre><code>
31255
31256 layout.addxtype({
31257        xtype : 'ContentPanel',
31258        region: 'west',
31259        items: [ .... ]
31260    }
31261 );
31262
31263 layout.addxtype({
31264         xtype : 'NestedLayoutPanel',
31265         region: 'west',
31266         layout: {
31267            center: { },
31268            west: { }   
31269         },
31270         items : [ ... list of content panels or nested layout panels.. ]
31271    }
31272 );
31273 </code></pre>
31274      * @param {Object} cfg Xtype definition of item to add.
31275      */
31276     addxtype : function(cfg)
31277     {
31278         // basically accepts a pannel...
31279         // can accept a layout region..!?!?
31280         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31281         
31282         if (!cfg.xtype.match(/Panel$/)) {
31283             return false;
31284         }
31285         var ret = false;
31286         
31287         if (typeof(cfg.region) == 'undefined') {
31288             Roo.log("Failed to add Panel, region was not set");
31289             Roo.log(cfg);
31290             return false;
31291         }
31292         var region = cfg.region;
31293         delete cfg.region;
31294         
31295           
31296         var xitems = [];
31297         if (cfg.items) {
31298             xitems = cfg.items;
31299             delete cfg.items;
31300         }
31301         var nb = false;
31302         
31303         switch(cfg.xtype) 
31304         {
31305             case 'ContentPanel':  // ContentPanel (el, cfg)
31306             case 'ScrollPanel':  // ContentPanel (el, cfg)
31307                 if(cfg.autoCreate) {
31308                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31309                 } else {
31310                     var el = this.el.createChild();
31311                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31312                 }
31313                 
31314                 this.add(region, ret);
31315                 break;
31316             
31317             
31318             case 'TreePanel': // our new panel!
31319                 cfg.el = this.el.createChild();
31320                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31321                 this.add(region, ret);
31322                 break;
31323             
31324             case 'NestedLayoutPanel': 
31325                 // create a new Layout (which is  a Border Layout...
31326                 var el = this.el.createChild();
31327                 var clayout = cfg.layout;
31328                 delete cfg.layout;
31329                 clayout.items   = clayout.items  || [];
31330                 // replace this exitems with the clayout ones..
31331                 xitems = clayout.items;
31332                  
31333                 
31334                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31335                     cfg.background = false;
31336                 }
31337                 var layout = new Roo.BorderLayout(el, clayout);
31338                 
31339                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
31340                 //console.log('adding nested layout panel '  + cfg.toSource());
31341                 this.add(region, ret);
31342                 nb = {}; /// find first...
31343                 break;
31344                 
31345             case 'GridPanel': 
31346             
31347                 // needs grid and region
31348                 
31349                 //var el = this.getRegion(region).el.createChild();
31350                 var el = this.el.createChild();
31351                 // create the grid first...
31352                 
31353                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
31354                 delete cfg.grid;
31355                 if (region == 'center' && this.active ) {
31356                     cfg.background = false;
31357                 }
31358                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
31359                 
31360                 this.add(region, ret);
31361                 if (cfg.background) {
31362                     ret.on('activate', function(gp) {
31363                         if (!gp.grid.rendered) {
31364                             gp.grid.render();
31365                         }
31366                     });
31367                 } else {
31368                     grid.render();
31369                 }
31370                 break;
31371            
31372                
31373                 
31374                 
31375             default: 
31376                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
31377                 return null;
31378              // GridPanel (grid, cfg)
31379             
31380         }
31381         this.beginUpdate();
31382         // add children..
31383         var region = '';
31384         var abn = {};
31385         Roo.each(xitems, function(i)  {
31386             region = nb && i.region ? i.region : false;
31387             
31388             var add = ret.addxtype(i);
31389            
31390             if (region) {
31391                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31392                 if (!i.background) {
31393                     abn[region] = nb[region] ;
31394                 }
31395             }
31396             
31397         });
31398         this.endUpdate();
31399
31400         // make the last non-background panel active..
31401         //if (nb) { Roo.log(abn); }
31402         if (nb) {
31403             
31404             for(var r in abn) {
31405                 region = this.getRegion(r);
31406                 if (region) {
31407                     // tried using nb[r], but it does not work..
31408                      
31409                     region.showPanel(abn[r]);
31410                    
31411                 }
31412             }
31413         }
31414         return ret;
31415         
31416     }
31417 });
31418
31419 /**
31420  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
31421  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
31422  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
31423  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
31424  * <pre><code>
31425 // shorthand
31426 var CP = Roo.ContentPanel;
31427
31428 var layout = Roo.BorderLayout.create({
31429     north: {
31430         initialSize: 25,
31431         titlebar: false,
31432         panels: [new CP("north", "North")]
31433     },
31434     west: {
31435         split:true,
31436         initialSize: 200,
31437         minSize: 175,
31438         maxSize: 400,
31439         titlebar: true,
31440         collapsible: true,
31441         panels: [new CP("west", {title: "West"})]
31442     },
31443     east: {
31444         split:true,
31445         initialSize: 202,
31446         minSize: 175,
31447         maxSize: 400,
31448         titlebar: true,
31449         collapsible: true,
31450         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
31451     },
31452     south: {
31453         split:true,
31454         initialSize: 100,
31455         minSize: 100,
31456         maxSize: 200,
31457         titlebar: true,
31458         collapsible: true,
31459         panels: [new CP("south", {title: "South", closable: true})]
31460     },
31461     center: {
31462         titlebar: true,
31463         autoScroll:true,
31464         resizeTabs: true,
31465         minTabWidth: 50,
31466         preferredTabWidth: 150,
31467         panels: [
31468             new CP("center1", {title: "Close Me", closable: true}),
31469             new CP("center2", {title: "Center Panel", closable: false})
31470         ]
31471     }
31472 }, document.body);
31473
31474 layout.getRegion("center").showPanel("center1");
31475 </code></pre>
31476  * @param config
31477  * @param targetEl
31478  */
31479 Roo.BorderLayout.create = function(config, targetEl){
31480     var layout = new Roo.BorderLayout(targetEl || document.body, config);
31481     layout.beginUpdate();
31482     var regions = Roo.BorderLayout.RegionFactory.validRegions;
31483     for(var j = 0, jlen = regions.length; j < jlen; j++){
31484         var lr = regions[j];
31485         if(layout.regions[lr] && config[lr].panels){
31486             var r = layout.regions[lr];
31487             var ps = config[lr].panels;
31488             layout.addTypedPanels(r, ps);
31489         }
31490     }
31491     layout.endUpdate();
31492     return layout;
31493 };
31494
31495 // private
31496 Roo.BorderLayout.RegionFactory = {
31497     // private
31498     validRegions : ["north","south","east","west","center"],
31499
31500     // private
31501     create : function(target, mgr, config){
31502         target = target.toLowerCase();
31503         if(config.lightweight || config.basic){
31504             return new Roo.BasicLayoutRegion(mgr, config, target);
31505         }
31506         switch(target){
31507             case "north":
31508                 return new Roo.NorthLayoutRegion(mgr, config);
31509             case "south":
31510                 return new Roo.SouthLayoutRegion(mgr, config);
31511             case "east":
31512                 return new Roo.EastLayoutRegion(mgr, config);
31513             case "west":
31514                 return new Roo.WestLayoutRegion(mgr, config);
31515             case "center":
31516                 return new Roo.CenterLayoutRegion(mgr, config);
31517         }
31518         throw 'Layout region "'+target+'" not supported.';
31519     }
31520 };/*
31521  * Based on:
31522  * Ext JS Library 1.1.1
31523  * Copyright(c) 2006-2007, Ext JS, LLC.
31524  *
31525  * Originally Released Under LGPL - original licence link has changed is not relivant.
31526  *
31527  * Fork - LGPL
31528  * <script type="text/javascript">
31529  */
31530  
31531 /**
31532  * @class Roo.BasicLayoutRegion
31533  * @extends Roo.util.Observable
31534  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31535  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31536  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31537  */
31538 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
31539     this.mgr = mgr;
31540     this.position  = pos;
31541     this.events = {
31542         /**
31543          * @scope Roo.BasicLayoutRegion
31544          */
31545         
31546         /**
31547          * @event beforeremove
31548          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31549          * @param {Roo.LayoutRegion} this
31550          * @param {Roo.ContentPanel} panel The panel
31551          * @param {Object} e The cancel event object
31552          */
31553         "beforeremove" : true,
31554         /**
31555          * @event invalidated
31556          * Fires when the layout for this region is changed.
31557          * @param {Roo.LayoutRegion} this
31558          */
31559         "invalidated" : true,
31560         /**
31561          * @event visibilitychange
31562          * Fires when this region is shown or hidden 
31563          * @param {Roo.LayoutRegion} this
31564          * @param {Boolean} visibility true or false
31565          */
31566         "visibilitychange" : true,
31567         /**
31568          * @event paneladded
31569          * Fires when a panel is added. 
31570          * @param {Roo.LayoutRegion} this
31571          * @param {Roo.ContentPanel} panel The panel
31572          */
31573         "paneladded" : true,
31574         /**
31575          * @event panelremoved
31576          * Fires when a panel is removed. 
31577          * @param {Roo.LayoutRegion} this
31578          * @param {Roo.ContentPanel} panel The panel
31579          */
31580         "panelremoved" : true,
31581         /**
31582          * @event collapsed
31583          * Fires when this region is collapsed.
31584          * @param {Roo.LayoutRegion} this
31585          */
31586         "collapsed" : true,
31587         /**
31588          * @event expanded
31589          * Fires when this region is expanded.
31590          * @param {Roo.LayoutRegion} this
31591          */
31592         "expanded" : true,
31593         /**
31594          * @event slideshow
31595          * Fires when this region is slid into view.
31596          * @param {Roo.LayoutRegion} this
31597          */
31598         "slideshow" : true,
31599         /**
31600          * @event slidehide
31601          * Fires when this region slides out of view. 
31602          * @param {Roo.LayoutRegion} this
31603          */
31604         "slidehide" : true,
31605         /**
31606          * @event panelactivated
31607          * Fires when a panel is activated. 
31608          * @param {Roo.LayoutRegion} this
31609          * @param {Roo.ContentPanel} panel The activated panel
31610          */
31611         "panelactivated" : true,
31612         /**
31613          * @event resized
31614          * Fires when the user resizes this region. 
31615          * @param {Roo.LayoutRegion} this
31616          * @param {Number} newSize The new size (width for east/west, height for north/south)
31617          */
31618         "resized" : true
31619     };
31620     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31621     this.panels = new Roo.util.MixedCollection();
31622     this.panels.getKey = this.getPanelId.createDelegate(this);
31623     this.box = null;
31624     this.activePanel = null;
31625     // ensure listeners are added...
31626     
31627     if (config.listeners || config.events) {
31628         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
31629             listeners : config.listeners || {},
31630             events : config.events || {}
31631         });
31632     }
31633     
31634     if(skipConfig !== true){
31635         this.applyConfig(config);
31636     }
31637 };
31638
31639 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
31640     getPanelId : function(p){
31641         return p.getId();
31642     },
31643     
31644     applyConfig : function(config){
31645         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31646         this.config = config;
31647         
31648     },
31649     
31650     /**
31651      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31652      * the width, for horizontal (north, south) the height.
31653      * @param {Number} newSize The new width or height
31654      */
31655     resizeTo : function(newSize){
31656         var el = this.el ? this.el :
31657                  (this.activePanel ? this.activePanel.getEl() : null);
31658         if(el){
31659             switch(this.position){
31660                 case "east":
31661                 case "west":
31662                     el.setWidth(newSize);
31663                     this.fireEvent("resized", this, newSize);
31664                 break;
31665                 case "north":
31666                 case "south":
31667                     el.setHeight(newSize);
31668                     this.fireEvent("resized", this, newSize);
31669                 break;                
31670             }
31671         }
31672     },
31673     
31674     getBox : function(){
31675         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31676     },
31677     
31678     getMargins : function(){
31679         return this.margins;
31680     },
31681     
31682     updateBox : function(box){
31683         this.box = box;
31684         var el = this.activePanel.getEl();
31685         el.dom.style.left = box.x + "px";
31686         el.dom.style.top = box.y + "px";
31687         this.activePanel.setSize(box.width, box.height);
31688     },
31689     
31690     /**
31691      * Returns the container element for this region.
31692      * @return {Roo.Element}
31693      */
31694     getEl : function(){
31695         return this.activePanel;
31696     },
31697     
31698     /**
31699      * Returns true if this region is currently visible.
31700      * @return {Boolean}
31701      */
31702     isVisible : function(){
31703         return this.activePanel ? true : false;
31704     },
31705     
31706     setActivePanel : function(panel){
31707         panel = this.getPanel(panel);
31708         if(this.activePanel && this.activePanel != panel){
31709             this.activePanel.setActiveState(false);
31710             this.activePanel.getEl().setLeftTop(-10000,-10000);
31711         }
31712         this.activePanel = panel;
31713         panel.setActiveState(true);
31714         if(this.box){
31715             panel.setSize(this.box.width, this.box.height);
31716         }
31717         this.fireEvent("panelactivated", this, panel);
31718         this.fireEvent("invalidated");
31719     },
31720     
31721     /**
31722      * Show the specified panel.
31723      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31724      * @return {Roo.ContentPanel} The shown panel or null
31725      */
31726     showPanel : function(panel){
31727         if(panel = this.getPanel(panel)){
31728             this.setActivePanel(panel);
31729         }
31730         return panel;
31731     },
31732     
31733     /**
31734      * Get the active panel for this region.
31735      * @return {Roo.ContentPanel} The active panel or null
31736      */
31737     getActivePanel : function(){
31738         return this.activePanel;
31739     },
31740     
31741     /**
31742      * Add the passed ContentPanel(s)
31743      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31744      * @return {Roo.ContentPanel} The panel added (if only one was added)
31745      */
31746     add : function(panel){
31747         if(arguments.length > 1){
31748             for(var i = 0, len = arguments.length; i < len; i++) {
31749                 this.add(arguments[i]);
31750             }
31751             return null;
31752         }
31753         if(this.hasPanel(panel)){
31754             this.showPanel(panel);
31755             return panel;
31756         }
31757         var el = panel.getEl();
31758         if(el.dom.parentNode != this.mgr.el.dom){
31759             this.mgr.el.dom.appendChild(el.dom);
31760         }
31761         if(panel.setRegion){
31762             panel.setRegion(this);
31763         }
31764         this.panels.add(panel);
31765         el.setStyle("position", "absolute");
31766         if(!panel.background){
31767             this.setActivePanel(panel);
31768             if(this.config.initialSize && this.panels.getCount()==1){
31769                 this.resizeTo(this.config.initialSize);
31770             }
31771         }
31772         this.fireEvent("paneladded", this, panel);
31773         return panel;
31774     },
31775     
31776     /**
31777      * Returns true if the panel is in this region.
31778      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31779      * @return {Boolean}
31780      */
31781     hasPanel : function(panel){
31782         if(typeof panel == "object"){ // must be panel obj
31783             panel = panel.getId();
31784         }
31785         return this.getPanel(panel) ? true : false;
31786     },
31787     
31788     /**
31789      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31790      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31791      * @param {Boolean} preservePanel Overrides the config preservePanel option
31792      * @return {Roo.ContentPanel} The panel that was removed
31793      */
31794     remove : function(panel, preservePanel){
31795         panel = this.getPanel(panel);
31796         if(!panel){
31797             return null;
31798         }
31799         var e = {};
31800         this.fireEvent("beforeremove", this, panel, e);
31801         if(e.cancel === true){
31802             return null;
31803         }
31804         var panelId = panel.getId();
31805         this.panels.removeKey(panelId);
31806         return panel;
31807     },
31808     
31809     /**
31810      * Returns the panel specified or null if it's not in this region.
31811      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31812      * @return {Roo.ContentPanel}
31813      */
31814     getPanel : function(id){
31815         if(typeof id == "object"){ // must be panel obj
31816             return id;
31817         }
31818         return this.panels.get(id);
31819     },
31820     
31821     /**
31822      * Returns this regions position (north/south/east/west/center).
31823      * @return {String} 
31824      */
31825     getPosition: function(){
31826         return this.position;    
31827     }
31828 });