Roo/form/ComboBoxArray.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     var onStop = function(e){
3044         dragEl = null;
3045         clearProc();
3046     };
3047     
3048     var triggerRefresh = function(){
3049         if(ddm.dragCurrent){
3050              ddm.refreshCache(ddm.dragCurrent.groups);
3051         }
3052     };
3053     
3054     var doScroll = function(){
3055         if(ddm.dragCurrent){
3056             var dds = Roo.dd.ScrollManager;
3057             if(!dds.animate){
3058                 if(proc.el.scroll(proc.dir, dds.increment)){
3059                     triggerRefresh();
3060                 }
3061             }else{
3062                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3063             }
3064         }
3065     };
3066     
3067     var clearProc = function(){
3068         if(proc.id){
3069             clearInterval(proc.id);
3070         }
3071         proc.id = 0;
3072         proc.el = null;
3073         proc.dir = "";
3074     };
3075     
3076     var startProc = function(el, dir){
3077         clearProc();
3078         proc.el = el;
3079         proc.dir = dir;
3080         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3081     };
3082     
3083     var onFire = function(e, isDrop){
3084         if(isDrop || !ddm.dragCurrent){ return; }
3085         var dds = Roo.dd.ScrollManager;
3086         if(!dragEl || dragEl != ddm.dragCurrent){
3087             dragEl = ddm.dragCurrent;
3088             // refresh regions on drag start
3089             dds.refreshCache();
3090         }
3091         
3092         var xy = Roo.lib.Event.getXY(e);
3093         var pt = new Roo.lib.Point(xy[0], xy[1]);
3094         for(var id in els){
3095             var el = els[id], r = el._region;
3096             if(r && r.contains(pt) && el.isScrollable()){
3097                 if(r.bottom - pt.y <= dds.thresh){
3098                     if(proc.el != el){
3099                         startProc(el, "down");
3100                     }
3101                     return;
3102                 }else if(r.right - pt.x <= dds.thresh){
3103                     if(proc.el != el){
3104                         startProc(el, "left");
3105                     }
3106                     return;
3107                 }else if(pt.y - r.top <= dds.thresh){
3108                     if(proc.el != el){
3109                         startProc(el, "up");
3110                     }
3111                     return;
3112                 }else if(pt.x - r.left <= dds.thresh){
3113                     if(proc.el != el){
3114                         startProc(el, "right");
3115                     }
3116                     return;
3117                 }
3118             }
3119         }
3120         clearProc();
3121     };
3122     
3123     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3124     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3125     
3126     return {
3127         /**
3128          * Registers new overflow element(s) to auto scroll
3129          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3130          */
3131         register : function(el){
3132             if(el instanceof Array){
3133                 for(var i = 0, len = el.length; i < len; i++) {
3134                         this.register(el[i]);
3135                 }
3136             }else{
3137                 el = Roo.get(el);
3138                 els[el.id] = el;
3139             }
3140         },
3141         
3142         /**
3143          * Unregisters overflow element(s) so they are no longer scrolled
3144          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3145          */
3146         unregister : function(el){
3147             if(el instanceof Array){
3148                 for(var i = 0, len = el.length; i < len; i++) {
3149                         this.unregister(el[i]);
3150                 }
3151             }else{
3152                 el = Roo.get(el);
3153                 delete els[el.id];
3154             }
3155         },
3156         
3157         /**
3158          * The number of pixels from the edge of a container the pointer needs to be to 
3159          * trigger scrolling (defaults to 25)
3160          * @type Number
3161          */
3162         thresh : 25,
3163         
3164         /**
3165          * The number of pixels to scroll in each scroll increment (defaults to 50)
3166          * @type Number
3167          */
3168         increment : 100,
3169         
3170         /**
3171          * The frequency of scrolls in milliseconds (defaults to 500)
3172          * @type Number
3173          */
3174         frequency : 500,
3175         
3176         /**
3177          * True to animate the scroll (defaults to true)
3178          * @type Boolean
3179          */
3180         animate: true,
3181         
3182         /**
3183          * The animation duration in seconds - 
3184          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3185          * @type Number
3186          */
3187         animDuration: .4,
3188         
3189         /**
3190          * Manually trigger a cache refresh.
3191          */
3192         refreshCache : function(){
3193             for(var id in els){
3194                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3195                     els[id]._region = els[id].getRegion();
3196                 }
3197             }
3198         }
3199     };
3200 }();/*
3201  * Based on:
3202  * Ext JS Library 1.1.1
3203  * Copyright(c) 2006-2007, Ext JS, LLC.
3204  *
3205  * Originally Released Under LGPL - original licence link has changed is not relivant.
3206  *
3207  * Fork - LGPL
3208  * <script type="text/javascript">
3209  */
3210  
3211
3212 /**
3213  * @class Roo.dd.Registry
3214  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3215  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3216  * @singleton
3217  */
3218 Roo.dd.Registry = function(){
3219     var elements = {}; 
3220     var handles = {}; 
3221     var autoIdSeed = 0;
3222
3223     var getId = function(el, autogen){
3224         if(typeof el == "string"){
3225             return el;
3226         }
3227         var id = el.id;
3228         if(!id && autogen !== false){
3229             id = "roodd-" + (++autoIdSeed);
3230             el.id = id;
3231         }
3232         return id;
3233     };
3234     
3235     return {
3236     /**
3237      * Register a drag drop element
3238      * @param {String|HTMLElement} element The id or DOM node to register
3239      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3240      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3241      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3242      * populated in the data object (if applicable):
3243      * <pre>
3244 Value      Description<br />
3245 ---------  ------------------------------------------<br />
3246 handles    Array of DOM nodes that trigger dragging<br />
3247            for the element being registered<br />
3248 isHandle   True if the element passed in triggers<br />
3249            dragging itself, else false
3250 </pre>
3251      */
3252         register : function(el, data){
3253             data = data || {};
3254             if(typeof el == "string"){
3255                 el = document.getElementById(el);
3256             }
3257             data.ddel = el;
3258             elements[getId(el)] = data;
3259             if(data.isHandle !== false){
3260                 handles[data.ddel.id] = data;
3261             }
3262             if(data.handles){
3263                 var hs = data.handles;
3264                 for(var i = 0, len = hs.length; i < len; i++){
3265                         handles[getId(hs[i])] = data;
3266                 }
3267             }
3268         },
3269
3270     /**
3271      * Unregister a drag drop element
3272      * @param {String|HTMLElement}  element The id or DOM node to unregister
3273      */
3274         unregister : function(el){
3275             var id = getId(el, false);
3276             var data = elements[id];
3277             if(data){
3278                 delete elements[id];
3279                 if(data.handles){
3280                     var hs = data.handles;
3281                     for(var i = 0, len = hs.length; i < len; i++){
3282                         delete handles[getId(hs[i], false)];
3283                     }
3284                 }
3285             }
3286         },
3287
3288     /**
3289      * Returns the handle registered for a DOM Node by id
3290      * @param {String|HTMLElement} id The DOM node or id to look up
3291      * @return {Object} handle The custom handle data
3292      */
3293         getHandle : function(id){
3294             if(typeof id != "string"){ // must be element?
3295                 id = id.id;
3296             }
3297             return handles[id];
3298         },
3299
3300     /**
3301      * Returns the handle that is registered for the DOM node that is the target of the event
3302      * @param {Event} e The event
3303      * @return {Object} handle The custom handle data
3304      */
3305         getHandleFromEvent : function(e){
3306             var t = Roo.lib.Event.getTarget(e);
3307             return t ? handles[t.id] : null;
3308         },
3309
3310     /**
3311      * Returns a custom data object that is registered for a DOM node by id
3312      * @param {String|HTMLElement} id The DOM node or id to look up
3313      * @return {Object} data The custom data
3314      */
3315         getTarget : function(id){
3316             if(typeof id != "string"){ // must be element?
3317                 id = id.id;
3318             }
3319             return elements[id];
3320         },
3321
3322     /**
3323      * Returns a custom data object that is registered for the DOM node that is the target of the event
3324      * @param {Event} e The event
3325      * @return {Object} data The custom data
3326      */
3327         getTargetFromEvent : function(e){
3328             var t = Roo.lib.Event.getTarget(e);
3329             return t ? elements[t.id] || handles[t.id] : null;
3330         }
3331     };
3332 }();/*
3333  * Based on:
3334  * Ext JS Library 1.1.1
3335  * Copyright(c) 2006-2007, Ext JS, LLC.
3336  *
3337  * Originally Released Under LGPL - original licence link has changed is not relivant.
3338  *
3339  * Fork - LGPL
3340  * <script type="text/javascript">
3341  */
3342  
3343
3344 /**
3345  * @class Roo.dd.StatusProxy
3346  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3347  * default drag proxy used by all Roo.dd components.
3348  * @constructor
3349  * @param {Object} config
3350  */
3351 Roo.dd.StatusProxy = function(config){
3352     Roo.apply(this, config);
3353     this.id = this.id || Roo.id();
3354     this.el = new Roo.Layer({
3355         dh: {
3356             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3357                 {tag: "div", cls: "x-dd-drop-icon"},
3358                 {tag: "div", cls: "x-dd-drag-ghost"}
3359             ]
3360         }, 
3361         shadow: !config || config.shadow !== false
3362     });
3363     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3364     this.dropStatus = this.dropNotAllowed;
3365 };
3366
3367 Roo.dd.StatusProxy.prototype = {
3368     /**
3369      * @cfg {String} dropAllowed
3370      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3371      */
3372     dropAllowed : "x-dd-drop-ok",
3373     /**
3374      * @cfg {String} dropNotAllowed
3375      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3376      */
3377     dropNotAllowed : "x-dd-drop-nodrop",
3378
3379     /**
3380      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3381      * over the current target element.
3382      * @param {String} cssClass The css class for the new drop status indicator image
3383      */
3384     setStatus : function(cssClass){
3385         cssClass = cssClass || this.dropNotAllowed;
3386         if(this.dropStatus != cssClass){
3387             this.el.replaceClass(this.dropStatus, cssClass);
3388             this.dropStatus = cssClass;
3389         }
3390     },
3391
3392     /**
3393      * Resets the status indicator to the default dropNotAllowed value
3394      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3395      */
3396     reset : function(clearGhost){
3397         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3398         this.dropStatus = this.dropNotAllowed;
3399         if(clearGhost){
3400             this.ghost.update("");
3401         }
3402     },
3403
3404     /**
3405      * Updates the contents of the ghost element
3406      * @param {String} html The html that will replace the current innerHTML of the ghost element
3407      */
3408     update : function(html){
3409         if(typeof html == "string"){
3410             this.ghost.update(html);
3411         }else{
3412             this.ghost.update("");
3413             html.style.margin = "0";
3414             this.ghost.dom.appendChild(html);
3415         }
3416         // ensure float = none set?? cant remember why though.
3417         var el = this.ghost.dom.firstChild;
3418                 if(el){
3419                         Roo.fly(el).setStyle('float', 'none');
3420                 }
3421     },
3422     
3423     /**
3424      * Returns the underlying proxy {@link Roo.Layer}
3425      * @return {Roo.Layer} el
3426     */
3427     getEl : function(){
3428         return this.el;
3429     },
3430
3431     /**
3432      * Returns the ghost element
3433      * @return {Roo.Element} el
3434      */
3435     getGhost : function(){
3436         return this.ghost;
3437     },
3438
3439     /**
3440      * Hides the proxy
3441      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3442      */
3443     hide : function(clear){
3444         this.el.hide();
3445         if(clear){
3446             this.reset(true);
3447         }
3448     },
3449
3450     /**
3451      * Stops the repair animation if it's currently running
3452      */
3453     stop : function(){
3454         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3455             this.anim.stop();
3456         }
3457     },
3458
3459     /**
3460      * Displays this proxy
3461      */
3462     show : function(){
3463         this.el.show();
3464     },
3465
3466     /**
3467      * Force the Layer to sync its shadow and shim positions to the element
3468      */
3469     sync : function(){
3470         this.el.sync();
3471     },
3472
3473     /**
3474      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3475      * invalid drop operation by the item being dragged.
3476      * @param {Array} xy The XY position of the element ([x, y])
3477      * @param {Function} callback The function to call after the repair is complete
3478      * @param {Object} scope The scope in which to execute the callback
3479      */
3480     repair : function(xy, callback, scope){
3481         this.callback = callback;
3482         this.scope = scope;
3483         if(xy && this.animRepair !== false){
3484             this.el.addClass("x-dd-drag-repair");
3485             this.el.hideUnders(true);
3486             this.anim = this.el.shift({
3487                 duration: this.repairDuration || .5,
3488                 easing: 'easeOut',
3489                 xy: xy,
3490                 stopFx: true,
3491                 callback: this.afterRepair,
3492                 scope: this
3493             });
3494         }else{
3495             this.afterRepair();
3496         }
3497     },
3498
3499     // private
3500     afterRepair : function(){
3501         this.hide(true);
3502         if(typeof this.callback == "function"){
3503             this.callback.call(this.scope || this);
3504         }
3505         this.callback = null;
3506         this.scope = null;
3507     }
3508 };/*
3509  * Based on:
3510  * Ext JS Library 1.1.1
3511  * Copyright(c) 2006-2007, Ext JS, LLC.
3512  *
3513  * Originally Released Under LGPL - original licence link has changed is not relivant.
3514  *
3515  * Fork - LGPL
3516  * <script type="text/javascript">
3517  */
3518
3519 /**
3520  * @class Roo.dd.DragSource
3521  * @extends Roo.dd.DDProxy
3522  * A simple class that provides the basic implementation needed to make any element draggable.
3523  * @constructor
3524  * @param {String/HTMLElement/Element} el The container element
3525  * @param {Object} config
3526  */
3527 Roo.dd.DragSource = function(el, config){
3528     this.el = Roo.get(el);
3529     this.dragData = {};
3530     
3531     Roo.apply(this, config);
3532     
3533     if(!this.proxy){
3534         this.proxy = new Roo.dd.StatusProxy();
3535     }
3536
3537     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3538           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3539     
3540     this.dragging = false;
3541 };
3542
3543 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3544     /**
3545      * @cfg {String} dropAllowed
3546      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3547      */
3548     dropAllowed : "x-dd-drop-ok",
3549     /**
3550      * @cfg {String} dropNotAllowed
3551      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3552      */
3553     dropNotAllowed : "x-dd-drop-nodrop",
3554
3555     /**
3556      * Returns the data object associated with this drag source
3557      * @return {Object} data An object containing arbitrary data
3558      */
3559     getDragData : function(e){
3560         return this.dragData;
3561     },
3562
3563     // private
3564     onDragEnter : function(e, id){
3565         var target = Roo.dd.DragDropMgr.getDDById(id);
3566         this.cachedTarget = target;
3567         if(this.beforeDragEnter(target, e, id) !== false){
3568             if(target.isNotifyTarget){
3569                 var status = target.notifyEnter(this, e, this.dragData);
3570                 this.proxy.setStatus(status);
3571             }else{
3572                 this.proxy.setStatus(this.dropAllowed);
3573             }
3574             
3575             if(this.afterDragEnter){
3576                 /**
3577                  * An empty function by default, but provided so that you can perform a custom action
3578                  * when the dragged item enters the drop target by providing an implementation.
3579                  * @param {Roo.dd.DragDrop} target The drop target
3580                  * @param {Event} e The event object
3581                  * @param {String} id The id of the dragged element
3582                  * @method afterDragEnter
3583                  */
3584                 this.afterDragEnter(target, e, id);
3585             }
3586         }
3587     },
3588
3589     /**
3590      * An empty function by default, but provided so that you can perform a custom action
3591      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3592      * @param {Roo.dd.DragDrop} target The drop target
3593      * @param {Event} e The event object
3594      * @param {String} id The id of the dragged element
3595      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3596      */
3597     beforeDragEnter : function(target, e, id){
3598         return true;
3599     },
3600
3601     // private
3602     alignElWithMouse: function() {
3603         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3604         this.proxy.sync();
3605     },
3606
3607     // private
3608     onDragOver : function(e, id){
3609         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3610         if(this.beforeDragOver(target, e, id) !== false){
3611             if(target.isNotifyTarget){
3612                 var status = target.notifyOver(this, e, this.dragData);
3613                 this.proxy.setStatus(status);
3614             }
3615
3616             if(this.afterDragOver){
3617                 /**
3618                  * An empty function by default, but provided so that you can perform a custom action
3619                  * while the dragged item is over the drop target by providing an implementation.
3620                  * @param {Roo.dd.DragDrop} target The drop target
3621                  * @param {Event} e The event object
3622                  * @param {String} id The id of the dragged element
3623                  * @method afterDragOver
3624                  */
3625                 this.afterDragOver(target, e, id);
3626             }
3627         }
3628     },
3629
3630     /**
3631      * An empty function by default, but provided so that you can perform a custom action
3632      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3633      * @param {Roo.dd.DragDrop} target The drop target
3634      * @param {Event} e The event object
3635      * @param {String} id The id of the dragged element
3636      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3637      */
3638     beforeDragOver : function(target, e, id){
3639         return true;
3640     },
3641
3642     // private
3643     onDragOut : function(e, id){
3644         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3645         if(this.beforeDragOut(target, e, id) !== false){
3646             if(target.isNotifyTarget){
3647                 target.notifyOut(this, e, this.dragData);
3648             }
3649             this.proxy.reset();
3650             if(this.afterDragOut){
3651                 /**
3652                  * An empty function by default, but provided so that you can perform a custom action
3653                  * after the dragged item is dragged out of the target without dropping.
3654                  * @param {Roo.dd.DragDrop} target The drop target
3655                  * @param {Event} e The event object
3656                  * @param {String} id The id of the dragged element
3657                  * @method afterDragOut
3658                  */
3659                 this.afterDragOut(target, e, id);
3660             }
3661         }
3662         this.cachedTarget = null;
3663     },
3664
3665     /**
3666      * An empty function by default, but provided so that you can perform a custom action before the dragged
3667      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3668      * @param {Roo.dd.DragDrop} target The drop target
3669      * @param {Event} e The event object
3670      * @param {String} id The id of the dragged element
3671      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3672      */
3673     beforeDragOut : function(target, e, id){
3674         return true;
3675     },
3676     
3677     // private
3678     onDragDrop : function(e, id){
3679         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3680         if(this.beforeDragDrop(target, e, id) !== false){
3681             if(target.isNotifyTarget){
3682                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3683                     this.onValidDrop(target, e, id);
3684                 }else{
3685                     this.onInvalidDrop(target, e, id);
3686                 }
3687             }else{
3688                 this.onValidDrop(target, e, id);
3689             }
3690             
3691             if(this.afterDragDrop){
3692                 /**
3693                  * An empty function by default, but provided so that you can perform a custom action
3694                  * after a valid drag drop has occurred by providing an implementation.
3695                  * @param {Roo.dd.DragDrop} target The drop target
3696                  * @param {Event} e The event object
3697                  * @param {String} id The id of the dropped element
3698                  * @method afterDragDrop
3699                  */
3700                 this.afterDragDrop(target, e, id);
3701             }
3702         }
3703         delete this.cachedTarget;
3704     },
3705
3706     /**
3707      * An empty function by default, but provided so that you can perform a custom action before the dragged
3708      * item is dropped onto the target and optionally cancel the onDragDrop.
3709      * @param {Roo.dd.DragDrop} target The drop target
3710      * @param {Event} e The event object
3711      * @param {String} id The id of the dragged element
3712      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3713      */
3714     beforeDragDrop : function(target, e, id){
3715         return true;
3716     },
3717
3718     // private
3719     onValidDrop : function(target, e, id){
3720         this.hideProxy();
3721         if(this.afterValidDrop){
3722             /**
3723              * An empty function by default, but provided so that you can perform a custom action
3724              * after a valid drop has occurred by providing an implementation.
3725              * @param {Object} target The target DD 
3726              * @param {Event} e The event object
3727              * @param {String} id The id of the dropped element
3728              * @method afterInvalidDrop
3729              */
3730             this.afterValidDrop(target, e, id);
3731         }
3732     },
3733
3734     // private
3735     getRepairXY : function(e, data){
3736         return this.el.getXY();  
3737     },
3738
3739     // private
3740     onInvalidDrop : function(target, e, id){
3741         this.beforeInvalidDrop(target, e, id);
3742         if(this.cachedTarget){
3743             if(this.cachedTarget.isNotifyTarget){
3744                 this.cachedTarget.notifyOut(this, e, this.dragData);
3745             }
3746             this.cacheTarget = null;
3747         }
3748         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3749
3750         if(this.afterInvalidDrop){
3751             /**
3752              * An empty function by default, but provided so that you can perform a custom action
3753              * after an invalid drop has occurred by providing an implementation.
3754              * @param {Event} e The event object
3755              * @param {String} id The id of the dropped element
3756              * @method afterInvalidDrop
3757              */
3758             this.afterInvalidDrop(e, id);
3759         }
3760     },
3761
3762     // private
3763     afterRepair : function(){
3764         if(Roo.enableFx){
3765             this.el.highlight(this.hlColor || "c3daf9");
3766         }
3767         this.dragging = false;
3768     },
3769
3770     /**
3771      * An empty function by default, but provided so that you can perform a custom action after an invalid
3772      * drop has occurred.
3773      * @param {Roo.dd.DragDrop} target The drop target
3774      * @param {Event} e The event object
3775      * @param {String} id The id of the dragged element
3776      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3777      */
3778     beforeInvalidDrop : function(target, e, id){
3779         return true;
3780     },
3781
3782     // private
3783     handleMouseDown : function(e){
3784         if(this.dragging) {
3785             return;
3786         }
3787         var data = this.getDragData(e);
3788         if(data && this.onBeforeDrag(data, e) !== false){
3789             this.dragData = data;
3790             this.proxy.stop();
3791             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3792         } 
3793     },
3794
3795     /**
3796      * An empty function by default, but provided so that you can perform a custom action before the initial
3797      * drag event begins and optionally cancel it.
3798      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3799      * @param {Event} e The event object
3800      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3801      */
3802     onBeforeDrag : function(data, e){
3803         return true;
3804     },
3805
3806     /**
3807      * An empty function by default, but provided so that you can perform a custom action once the initial
3808      * drag event has begun.  The drag cannot be canceled from this function.
3809      * @param {Number} x The x position of the click on the dragged object
3810      * @param {Number} y The y position of the click on the dragged object
3811      */
3812     onStartDrag : Roo.emptyFn,
3813
3814     // private - YUI override
3815     startDrag : function(x, y){
3816         this.proxy.reset();
3817         this.dragging = true;
3818         this.proxy.update("");
3819         this.onInitDrag(x, y);
3820         this.proxy.show();
3821     },
3822
3823     // private
3824     onInitDrag : function(x, y){
3825         var clone = this.el.dom.cloneNode(true);
3826         clone.id = Roo.id(); // prevent duplicate ids
3827         this.proxy.update(clone);
3828         this.onStartDrag(x, y);
3829         return true;
3830     },
3831
3832     /**
3833      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3834      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3835      */
3836     getProxy : function(){
3837         return this.proxy;  
3838     },
3839
3840     /**
3841      * Hides the drag source's {@link Roo.dd.StatusProxy}
3842      */
3843     hideProxy : function(){
3844         this.proxy.hide();  
3845         this.proxy.reset(true);
3846         this.dragging = false;
3847     },
3848
3849     // private
3850     triggerCacheRefresh : function(){
3851         Roo.dd.DDM.refreshCache(this.groups);
3852     },
3853
3854     // private - override to prevent hiding
3855     b4EndDrag: function(e) {
3856     },
3857
3858     // private - override to prevent moving
3859     endDrag : function(e){
3860         this.onEndDrag(this.dragData, e);
3861     },
3862
3863     // private
3864     onEndDrag : function(data, e){
3865     },
3866     
3867     // private - pin to cursor
3868     autoOffset : function(x, y) {
3869         this.setDelta(-12, -20);
3870     }    
3871 });/*
3872  * Based on:
3873  * Ext JS Library 1.1.1
3874  * Copyright(c) 2006-2007, Ext JS, LLC.
3875  *
3876  * Originally Released Under LGPL - original licence link has changed is not relivant.
3877  *
3878  * Fork - LGPL
3879  * <script type="text/javascript">
3880  */
3881
3882
3883 /**
3884  * @class Roo.dd.DropTarget
3885  * @extends Roo.dd.DDTarget
3886  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3887  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3888  * @constructor
3889  * @param {String/HTMLElement/Element} el The container element
3890  * @param {Object} config
3891  */
3892 Roo.dd.DropTarget = function(el, config){
3893     this.el = Roo.get(el);
3894     
3895     var listeners = false; ;
3896     if (config && config.listeners) {
3897         listeners= config.listeners;
3898         delete config.listeners;
3899     }
3900     Roo.apply(this, config);
3901     
3902     if(this.containerScroll){
3903         Roo.dd.ScrollManager.register(this.el);
3904     }
3905     this.addEvents( {
3906          /**
3907          * @scope Roo.dd.DropTarget
3908          */
3909          
3910          /**
3911          * @event enter
3912          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3913          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3914          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3915          * 
3916          * IMPORTANT : it should set this.overClass and this.dropAllowed
3917          * 
3918          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3919          * @param {Event} e The event
3920          * @param {Object} data An object containing arbitrary data supplied by the drag source
3921          */
3922         "enter" : true,
3923         
3924          /**
3925          * @event over
3926          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3927          * This method will be called on every mouse movement while the drag source is over the drop target.
3928          * This default implementation simply returns the dropAllowed config value.
3929          * 
3930          * IMPORTANT : it should set this.dropAllowed
3931          * 
3932          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3933          * @param {Event} e The event
3934          * @param {Object} data An object containing arbitrary data supplied by the drag source
3935          
3936          */
3937         "over" : true,
3938         /**
3939          * @event out
3940          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3941          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3942          * overClass (if any) from the drop element.
3943          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3944          * @param {Event} e The event
3945          * @param {Object} data An object containing arbitrary data supplied by the drag source
3946          */
3947          "out" : true,
3948          
3949         /**
3950          * @event drop
3951          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3952          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3953          * implementation that does something to process the drop event and returns true so that the drag source's
3954          * repair action does not run.
3955          * 
3956          * IMPORTANT : it should set this.success
3957          * 
3958          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3959          * @param {Event} e The event
3960          * @param {Object} data An object containing arbitrary data supplied by the drag source
3961         */
3962          "drop" : true
3963     });
3964             
3965      
3966     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3967         this.el.dom, 
3968         this.ddGroup || this.group,
3969         {
3970             isTarget: true,
3971             listeners : listeners || {} 
3972            
3973         
3974         }
3975     );
3976
3977 };
3978
3979 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3980     /**
3981      * @cfg {String} overClass
3982      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3983      */
3984      /**
3985      * @cfg {String} ddGroup
3986      * The drag drop group to handle drop events for
3987      */
3988      
3989     /**
3990      * @cfg {String} dropAllowed
3991      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3992      */
3993     dropAllowed : "x-dd-drop-ok",
3994     /**
3995      * @cfg {String} dropNotAllowed
3996      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3997      */
3998     dropNotAllowed : "x-dd-drop-nodrop",
3999     /**
4000      * @cfg {boolean} success
4001      * set this after drop listener.. 
4002      */
4003     success : false,
4004     /**
4005      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4006      * if the drop point is valid for over/enter..
4007      */
4008     valid : false,
4009     // private
4010     isTarget : true,
4011
4012     // private
4013     isNotifyTarget : true,
4014     
4015     /**
4016      * @hide
4017      */
4018     notifyEnter : function(dd, e, data)
4019     {
4020         this.valid = true;
4021         this.fireEvent('enter', dd, e, data);
4022         if(this.overClass){
4023             this.el.addClass(this.overClass);
4024         }
4025         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4026             this.valid ? this.dropAllowed : this.dropNotAllowed
4027         );
4028     },
4029
4030     /**
4031      * @hide
4032      */
4033     notifyOver : function(dd, e, data)
4034     {
4035         this.valid = true;
4036         this.fireEvent('over', dd, e, data);
4037         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4038             this.valid ? this.dropAllowed : this.dropNotAllowed
4039         );
4040     },
4041
4042     /**
4043      * @hide
4044      */
4045     notifyOut : function(dd, e, data)
4046     {
4047         this.fireEvent('out', dd, e, data);
4048         if(this.overClass){
4049             this.el.removeClass(this.overClass);
4050         }
4051     },
4052
4053     /**
4054      * @hide
4055      */
4056     notifyDrop : function(dd, e, data)
4057     {
4058         this.success = false;
4059         this.fireEvent('drop', dd, e, data);
4060         return this.success;
4061     }
4062 });/*
4063  * Based on:
4064  * Ext JS Library 1.1.1
4065  * Copyright(c) 2006-2007, Ext JS, LLC.
4066  *
4067  * Originally Released Under LGPL - original licence link has changed is not relivant.
4068  *
4069  * Fork - LGPL
4070  * <script type="text/javascript">
4071  */
4072
4073
4074 /**
4075  * @class Roo.dd.DragZone
4076  * @extends Roo.dd.DragSource
4077  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4078  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4079  * @constructor
4080  * @param {String/HTMLElement/Element} el The container element
4081  * @param {Object} config
4082  */
4083 Roo.dd.DragZone = function(el, config){
4084     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4085     if(this.containerScroll){
4086         Roo.dd.ScrollManager.register(this.el);
4087     }
4088 };
4089
4090 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4091     /**
4092      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4093      * for auto scrolling during drag operations.
4094      */
4095     /**
4096      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4097      * method after a failed drop (defaults to "c3daf9" - light blue)
4098      */
4099
4100     /**
4101      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4102      * for a valid target to drag based on the mouse down. Override this method
4103      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4104      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4105      * @param {EventObject} e The mouse down event
4106      * @return {Object} The dragData
4107      */
4108     getDragData : function(e){
4109         return Roo.dd.Registry.getHandleFromEvent(e);
4110     },
4111     
4112     /**
4113      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4114      * this.dragData.ddel
4115      * @param {Number} x The x position of the click on the dragged object
4116      * @param {Number} y The y position of the click on the dragged object
4117      * @return {Boolean} true to continue the drag, false to cancel
4118      */
4119     onInitDrag : function(x, y){
4120         this.proxy.update(this.dragData.ddel.cloneNode(true));
4121         this.onStartDrag(x, y);
4122         return true;
4123     },
4124     
4125     /**
4126      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4127      */
4128     afterRepair : function(){
4129         if(Roo.enableFx){
4130             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4131         }
4132         this.dragging = false;
4133     },
4134
4135     /**
4136      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4137      * the XY of this.dragData.ddel
4138      * @param {EventObject} e The mouse up event
4139      * @return {Array} The xy location (e.g. [100, 200])
4140      */
4141     getRepairXY : function(e){
4142         return Roo.Element.fly(this.dragData.ddel).getXY();  
4143     }
4144 });/*
4145  * Based on:
4146  * Ext JS Library 1.1.1
4147  * Copyright(c) 2006-2007, Ext JS, LLC.
4148  *
4149  * Originally Released Under LGPL - original licence link has changed is not relivant.
4150  *
4151  * Fork - LGPL
4152  * <script type="text/javascript">
4153  */
4154 /**
4155  * @class Roo.dd.DropZone
4156  * @extends Roo.dd.DropTarget
4157  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4158  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4159  * @constructor
4160  * @param {String/HTMLElement/Element} el The container element
4161  * @param {Object} config
4162  */
4163 Roo.dd.DropZone = function(el, config){
4164     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4165 };
4166
4167 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4168     /**
4169      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4170      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4171      * provide your own custom lookup.
4172      * @param {Event} e The event
4173      * @return {Object} data The custom data
4174      */
4175     getTargetFromEvent : function(e){
4176         return Roo.dd.Registry.getTargetFromEvent(e);
4177     },
4178
4179     /**
4180      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4181      * that it has registered.  This method has no default implementation and should be overridden to provide
4182      * node-specific processing if necessary.
4183      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4184      * {@link #getTargetFromEvent} for this node)
4185      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4186      * @param {Event} e The event
4187      * @param {Object} data An object containing arbitrary data supplied by the drag source
4188      */
4189     onNodeEnter : function(n, dd, e, data){
4190         
4191     },
4192
4193     /**
4194      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4195      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4196      * overridden to provide the proper feedback.
4197      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4198      * {@link #getTargetFromEvent} for this node)
4199      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4200      * @param {Event} e The event
4201      * @param {Object} data An object containing arbitrary data supplied by the drag source
4202      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4203      * underlying {@link Roo.dd.StatusProxy} can be updated
4204      */
4205     onNodeOver : function(n, dd, e, data){
4206         return this.dropAllowed;
4207     },
4208
4209     /**
4210      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4211      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4212      * node-specific processing if necessary.
4213      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4214      * {@link #getTargetFromEvent} for this node)
4215      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4216      * @param {Event} e The event
4217      * @param {Object} data An object containing arbitrary data supplied by the drag source
4218      */
4219     onNodeOut : function(n, dd, e, data){
4220         
4221     },
4222
4223     /**
4224      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4225      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4226      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4227      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4228      * {@link #getTargetFromEvent} for this node)
4229      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4230      * @param {Event} e The event
4231      * @param {Object} data An object containing arbitrary data supplied by the drag source
4232      * @return {Boolean} True if the drop was valid, else false
4233      */
4234     onNodeDrop : function(n, dd, e, data){
4235         return false;
4236     },
4237
4238     /**
4239      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4240      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4241      * it should be overridden to provide the proper feedback if necessary.
4242      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4243      * @param {Event} e The event
4244      * @param {Object} data An object containing arbitrary data supplied by the drag source
4245      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4246      * underlying {@link Roo.dd.StatusProxy} can be updated
4247      */
4248     onContainerOver : function(dd, e, data){
4249         return this.dropNotAllowed;
4250     },
4251
4252     /**
4253      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4254      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4255      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4256      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4257      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4258      * @param {Event} e The event
4259      * @param {Object} data An object containing arbitrary data supplied by the drag source
4260      * @return {Boolean} True if the drop was valid, else false
4261      */
4262     onContainerDrop : function(dd, e, data){
4263         return false;
4264     },
4265
4266     /**
4267      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4268      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4269      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4270      * you should override this method and provide a custom implementation.
4271      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4272      * @param {Event} e The event
4273      * @param {Object} data An object containing arbitrary data supplied by the drag source
4274      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4275      * underlying {@link Roo.dd.StatusProxy} can be updated
4276      */
4277     notifyEnter : function(dd, e, data){
4278         return this.dropNotAllowed;
4279     },
4280
4281     /**
4282      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4283      * This method will be called on every mouse movement while the drag source is over the drop zone.
4284      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4285      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4286      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4287      * registered node, it will call {@link #onContainerOver}.
4288      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4289      * @param {Event} e The event
4290      * @param {Object} data An object containing arbitrary data supplied by the drag source
4291      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4292      * underlying {@link Roo.dd.StatusProxy} can be updated
4293      */
4294     notifyOver : function(dd, e, data){
4295         var n = this.getTargetFromEvent(e);
4296         if(!n){ // not over valid drop target
4297             if(this.lastOverNode){
4298                 this.onNodeOut(this.lastOverNode, dd, e, data);
4299                 this.lastOverNode = null;
4300             }
4301             return this.onContainerOver(dd, e, data);
4302         }
4303         if(this.lastOverNode != n){
4304             if(this.lastOverNode){
4305                 this.onNodeOut(this.lastOverNode, dd, e, data);
4306             }
4307             this.onNodeEnter(n, dd, e, data);
4308             this.lastOverNode = n;
4309         }
4310         return this.onNodeOver(n, dd, e, data);
4311     },
4312
4313     /**
4314      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4315      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4316      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4317      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4318      * @param {Event} e The event
4319      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4320      */
4321     notifyOut : function(dd, e, data){
4322         if(this.lastOverNode){
4323             this.onNodeOut(this.lastOverNode, dd, e, data);
4324             this.lastOverNode = null;
4325         }
4326     },
4327
4328     /**
4329      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4330      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4331      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4332      * otherwise it will call {@link #onContainerDrop}.
4333      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4334      * @param {Event} e The event
4335      * @param {Object} data An object containing arbitrary data supplied by the drag source
4336      * @return {Boolean} True if the drop was valid, else false
4337      */
4338     notifyDrop : function(dd, e, data){
4339         if(this.lastOverNode){
4340             this.onNodeOut(this.lastOverNode, dd, e, data);
4341             this.lastOverNode = null;
4342         }
4343         var n = this.getTargetFromEvent(e);
4344         return n ?
4345             this.onNodeDrop(n, dd, e, data) :
4346             this.onContainerDrop(dd, e, data);
4347     },
4348
4349     // private
4350     triggerCacheRefresh : function(){
4351         Roo.dd.DDM.refreshCache(this.groups);
4352     }  
4353 });/*
4354  * Based on:
4355  * Ext JS Library 1.1.1
4356  * Copyright(c) 2006-2007, Ext JS, LLC.
4357  *
4358  * Originally Released Under LGPL - original licence link has changed is not relivant.
4359  *
4360  * Fork - LGPL
4361  * <script type="text/javascript">
4362  */
4363
4364
4365 /**
4366  * @class Roo.data.SortTypes
4367  * @singleton
4368  * Defines the default sorting (casting?) comparison functions used when sorting data.
4369  */
4370 Roo.data.SortTypes = {
4371     /**
4372      * Default sort that does nothing
4373      * @param {Mixed} s The value being converted
4374      * @return {Mixed} The comparison value
4375      */
4376     none : function(s){
4377         return s;
4378     },
4379     
4380     /**
4381      * The regular expression used to strip tags
4382      * @type {RegExp}
4383      * @property
4384      */
4385     stripTagsRE : /<\/?[^>]+>/gi,
4386     
4387     /**
4388      * Strips all HTML tags to sort on text only
4389      * @param {Mixed} s The value being converted
4390      * @return {String} The comparison value
4391      */
4392     asText : function(s){
4393         return String(s).replace(this.stripTagsRE, "");
4394     },
4395     
4396     /**
4397      * Strips all HTML tags to sort on text only - Case insensitive
4398      * @param {Mixed} s The value being converted
4399      * @return {String} The comparison value
4400      */
4401     asUCText : function(s){
4402         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4403     },
4404     
4405     /**
4406      * Case insensitive string
4407      * @param {Mixed} s The value being converted
4408      * @return {String} The comparison value
4409      */
4410     asUCString : function(s) {
4411         return String(s).toUpperCase();
4412     },
4413     
4414     /**
4415      * Date sorting
4416      * @param {Mixed} s The value being converted
4417      * @return {Number} The comparison value
4418      */
4419     asDate : function(s) {
4420         if(!s){
4421             return 0;
4422         }
4423         if(s instanceof Date){
4424             return s.getTime();
4425         }
4426         return Date.parse(String(s));
4427     },
4428     
4429     /**
4430      * Float sorting
4431      * @param {Mixed} s The value being converted
4432      * @return {Float} The comparison value
4433      */
4434     asFloat : function(s) {
4435         var val = parseFloat(String(s).replace(/,/g, ""));
4436         if(isNaN(val)) val = 0;
4437         return val;
4438     },
4439     
4440     /**
4441      * Integer sorting
4442      * @param {Mixed} s The value being converted
4443      * @return {Number} The comparison value
4444      */
4445     asInt : function(s) {
4446         var val = parseInt(String(s).replace(/,/g, ""));
4447         if(isNaN(val)) val = 0;
4448         return val;
4449     }
4450 };/*
4451  * Based on:
4452  * Ext JS Library 1.1.1
4453  * Copyright(c) 2006-2007, Ext JS, LLC.
4454  *
4455  * Originally Released Under LGPL - original licence link has changed is not relivant.
4456  *
4457  * Fork - LGPL
4458  * <script type="text/javascript">
4459  */
4460
4461 /**
4462 * @class Roo.data.Record
4463  * Instances of this class encapsulate both record <em>definition</em> information, and record
4464  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4465  * to access Records cached in an {@link Roo.data.Store} object.<br>
4466  * <p>
4467  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4468  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4469  * objects.<br>
4470  * <p>
4471  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4472  * @constructor
4473  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4474  * {@link #create}. The parameters are the same.
4475  * @param {Array} data An associative Array of data values keyed by the field name.
4476  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4477  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4478  * not specified an integer id is generated.
4479  */
4480 Roo.data.Record = function(data, id){
4481     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4482     this.data = data;
4483 };
4484
4485 /**
4486  * Generate a constructor for a specific record layout.
4487  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4488  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4489  * Each field definition object may contain the following properties: <ul>
4490  * <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,
4491  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4492  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4493  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4494  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4495  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4496  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4497  * this may be omitted.</p></li>
4498  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4499  * <ul><li>auto (Default, implies no conversion)</li>
4500  * <li>string</li>
4501  * <li>int</li>
4502  * <li>float</li>
4503  * <li>boolean</li>
4504  * <li>date</li></ul></p></li>
4505  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4506  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4507  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4508  * by the Reader into an object that will be stored in the Record. It is passed the
4509  * following parameters:<ul>
4510  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4511  * </ul></p></li>
4512  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4513  * </ul>
4514  * <br>usage:<br><pre><code>
4515 var TopicRecord = Roo.data.Record.create(
4516     {name: 'title', mapping: 'topic_title'},
4517     {name: 'author', mapping: 'username'},
4518     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4519     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4520     {name: 'lastPoster', mapping: 'user2'},
4521     {name: 'excerpt', mapping: 'post_text'}
4522 );
4523
4524 var myNewRecord = new TopicRecord({
4525     title: 'Do my job please',
4526     author: 'noobie',
4527     totalPosts: 1,
4528     lastPost: new Date(),
4529     lastPoster: 'Animal',
4530     excerpt: 'No way dude!'
4531 });
4532 myStore.add(myNewRecord);
4533 </code></pre>
4534  * @method create
4535  * @static
4536  */
4537 Roo.data.Record.create = function(o){
4538     var f = function(){
4539         f.superclass.constructor.apply(this, arguments);
4540     };
4541     Roo.extend(f, Roo.data.Record);
4542     var p = f.prototype;
4543     p.fields = new Roo.util.MixedCollection(false, function(field){
4544         return field.name;
4545     });
4546     for(var i = 0, len = o.length; i < len; i++){
4547         p.fields.add(new Roo.data.Field(o[i]));
4548     }
4549     f.getField = function(name){
4550         return p.fields.get(name);  
4551     };
4552     return f;
4553 };
4554
4555 Roo.data.Record.AUTO_ID = 1000;
4556 Roo.data.Record.EDIT = 'edit';
4557 Roo.data.Record.REJECT = 'reject';
4558 Roo.data.Record.COMMIT = 'commit';
4559
4560 Roo.data.Record.prototype = {
4561     /**
4562      * Readonly flag - true if this record has been modified.
4563      * @type Boolean
4564      */
4565     dirty : false,
4566     editing : false,
4567     error: null,
4568     modified: null,
4569
4570     // private
4571     join : function(store){
4572         this.store = store;
4573     },
4574
4575     /**
4576      * Set the named field to the specified value.
4577      * @param {String} name The name of the field to set.
4578      * @param {Object} value The value to set the field to.
4579      */
4580     set : function(name, value){
4581         if(this.data[name] == value){
4582             return;
4583         }
4584         this.dirty = true;
4585         if(!this.modified){
4586             this.modified = {};
4587         }
4588         if(typeof this.modified[name] == 'undefined'){
4589             this.modified[name] = this.data[name];
4590         }
4591         this.data[name] = value;
4592         if(!this.editing && this.store){
4593             this.store.afterEdit(this);
4594         }       
4595     },
4596
4597     /**
4598      * Get the value of the named field.
4599      * @param {String} name The name of the field to get the value of.
4600      * @return {Object} The value of the field.
4601      */
4602     get : function(name){
4603         return this.data[name]; 
4604     },
4605
4606     // private
4607     beginEdit : function(){
4608         this.editing = true;
4609         this.modified = {}; 
4610     },
4611
4612     // private
4613     cancelEdit : function(){
4614         this.editing = false;
4615         delete this.modified;
4616     },
4617
4618     // private
4619     endEdit : function(){
4620         this.editing = false;
4621         if(this.dirty && this.store){
4622             this.store.afterEdit(this);
4623         }
4624     },
4625
4626     /**
4627      * Usually called by the {@link Roo.data.Store} which owns the Record.
4628      * Rejects all changes made to the Record since either creation, or the last commit operation.
4629      * Modified fields are reverted to their original values.
4630      * <p>
4631      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4632      * of reject operations.
4633      */
4634     reject : function(){
4635         var m = this.modified;
4636         for(var n in m){
4637             if(typeof m[n] != "function"){
4638                 this.data[n] = m[n];
4639             }
4640         }
4641         this.dirty = false;
4642         delete this.modified;
4643         this.editing = false;
4644         if(this.store){
4645             this.store.afterReject(this);
4646         }
4647     },
4648
4649     /**
4650      * Usually called by the {@link Roo.data.Store} which owns the Record.
4651      * Commits all changes made to the Record since either creation, or the last commit operation.
4652      * <p>
4653      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4654      * of commit operations.
4655      */
4656     commit : function(){
4657         this.dirty = false;
4658         delete this.modified;
4659         this.editing = false;
4660         if(this.store){
4661             this.store.afterCommit(this);
4662         }
4663     },
4664
4665     // private
4666     hasError : function(){
4667         return this.error != null;
4668     },
4669
4670     // private
4671     clearError : function(){
4672         this.error = null;
4673     },
4674
4675     /**
4676      * Creates a copy of this record.
4677      * @param {String} id (optional) A new record id if you don't want to use this record's id
4678      * @return {Record}
4679      */
4680     copy : function(newId) {
4681         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4682     }
4683 };/*
4684  * Based on:
4685  * Ext JS Library 1.1.1
4686  * Copyright(c) 2006-2007, Ext JS, LLC.
4687  *
4688  * Originally Released Under LGPL - original licence link has changed is not relivant.
4689  *
4690  * Fork - LGPL
4691  * <script type="text/javascript">
4692  */
4693
4694
4695
4696 /**
4697  * @class Roo.data.Store
4698  * @extends Roo.util.Observable
4699  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4700  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4701  * <p>
4702  * 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
4703  * has no knowledge of the format of the data returned by the Proxy.<br>
4704  * <p>
4705  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4706  * instances from the data object. These records are cached and made available through accessor functions.
4707  * @constructor
4708  * Creates a new Store.
4709  * @param {Object} config A config object containing the objects needed for the Store to access data,
4710  * and read the data into Records.
4711  */
4712 Roo.data.Store = function(config){
4713     this.data = new Roo.util.MixedCollection(false);
4714     this.data.getKey = function(o){
4715         return o.id;
4716     };
4717     this.baseParams = {};
4718     // private
4719     this.paramNames = {
4720         "start" : "start",
4721         "limit" : "limit",
4722         "sort" : "sort",
4723         "dir" : "dir",
4724         "multisort" : "_multisort"
4725     };
4726
4727     if(config && config.data){
4728         this.inlineData = config.data;
4729         delete config.data;
4730     }
4731
4732     Roo.apply(this, config);
4733     
4734     if(this.reader){ // reader passed
4735         this.reader = Roo.factory(this.reader, Roo.data);
4736         this.reader.xmodule = this.xmodule || false;
4737         if(!this.recordType){
4738             this.recordType = this.reader.recordType;
4739         }
4740         if(this.reader.onMetaChange){
4741             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4742         }
4743     }
4744
4745     if(this.recordType){
4746         this.fields = this.recordType.prototype.fields;
4747     }
4748     this.modified = [];
4749
4750     this.addEvents({
4751         /**
4752          * @event datachanged
4753          * Fires when the data cache has changed, and a widget which is using this Store
4754          * as a Record cache should refresh its view.
4755          * @param {Store} this
4756          */
4757         datachanged : true,
4758         /**
4759          * @event metachange
4760          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4761          * @param {Store} this
4762          * @param {Object} meta The JSON metadata
4763          */
4764         metachange : true,
4765         /**
4766          * @event add
4767          * Fires when Records have been added to the Store
4768          * @param {Store} this
4769          * @param {Roo.data.Record[]} records The array of Records added
4770          * @param {Number} index The index at which the record(s) were added
4771          */
4772         add : true,
4773         /**
4774          * @event remove
4775          * Fires when a Record has been removed from the Store
4776          * @param {Store} this
4777          * @param {Roo.data.Record} record The Record that was removed
4778          * @param {Number} index The index at which the record was removed
4779          */
4780         remove : true,
4781         /**
4782          * @event update
4783          * Fires when a Record has been updated
4784          * @param {Store} this
4785          * @param {Roo.data.Record} record The Record that was updated
4786          * @param {String} operation The update operation being performed.  Value may be one of:
4787          * <pre><code>
4788  Roo.data.Record.EDIT
4789  Roo.data.Record.REJECT
4790  Roo.data.Record.COMMIT
4791          * </code></pre>
4792          */
4793         update : true,
4794         /**
4795          * @event clear
4796          * Fires when the data cache has been cleared.
4797          * @param {Store} this
4798          */
4799         clear : true,
4800         /**
4801          * @event beforeload
4802          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4803          * the load action will be canceled.
4804          * @param {Store} this
4805          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4806          */
4807         beforeload : true,
4808         /**
4809          * @event load
4810          * Fires after a new set of Records has been loaded.
4811          * @param {Store} this
4812          * @param {Roo.data.Record[]} records The Records that were loaded
4813          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4814          */
4815         load : true,
4816         /**
4817          * @event loadexception
4818          * Fires if an exception occurs in the Proxy during loading.
4819          * Called with the signature of the Proxy's "loadexception" event.
4820          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4821          * 
4822          * @param {Proxy} 
4823          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4824          * @param {Object} load options 
4825          * @param {Object} jsonData from your request (normally this contains the Exception)
4826          */
4827         loadexception : true
4828     });
4829     
4830     if(this.proxy){
4831         this.proxy = Roo.factory(this.proxy, Roo.data);
4832         this.proxy.xmodule = this.xmodule || false;
4833         this.relayEvents(this.proxy,  ["loadexception"]);
4834     }
4835     this.sortToggle = {};
4836     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4837
4838     Roo.data.Store.superclass.constructor.call(this);
4839
4840     if(this.inlineData){
4841         this.loadData(this.inlineData);
4842         delete this.inlineData;
4843     }
4844 };
4845 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4846      /**
4847     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4848     * without a remote query - used by combo/forms at present.
4849     */
4850     
4851     /**
4852     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4853     */
4854     /**
4855     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4856     */
4857     /**
4858     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4859     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4860     */
4861     /**
4862     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4863     * on any HTTP request
4864     */
4865     /**
4866     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4867     */
4868     /**
4869     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4870     */
4871     multiSort: false,
4872     /**
4873     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4874     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4875     */
4876     remoteSort : false,
4877
4878     /**
4879     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4880      * loaded or when a record is removed. (defaults to false).
4881     */
4882     pruneModifiedRecords : false,
4883
4884     // private
4885     lastOptions : null,
4886
4887     /**
4888      * Add Records to the Store and fires the add event.
4889      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4890      */
4891     add : function(records){
4892         records = [].concat(records);
4893         for(var i = 0, len = records.length; i < len; i++){
4894             records[i].join(this);
4895         }
4896         var index = this.data.length;
4897         this.data.addAll(records);
4898         this.fireEvent("add", this, records, index);
4899     },
4900
4901     /**
4902      * Remove a Record from the Store and fires the remove event.
4903      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4904      */
4905     remove : function(record){
4906         var index = this.data.indexOf(record);
4907         this.data.removeAt(index);
4908         if(this.pruneModifiedRecords){
4909             this.modified.remove(record);
4910         }
4911         this.fireEvent("remove", this, record, index);
4912     },
4913
4914     /**
4915      * Remove all Records from the Store and fires the clear event.
4916      */
4917     removeAll : function(){
4918         this.data.clear();
4919         if(this.pruneModifiedRecords){
4920             this.modified = [];
4921         }
4922         this.fireEvent("clear", this);
4923     },
4924
4925     /**
4926      * Inserts Records to the Store at the given index and fires the add event.
4927      * @param {Number} index The start index at which to insert the passed Records.
4928      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4929      */
4930     insert : function(index, records){
4931         records = [].concat(records);
4932         for(var i = 0, len = records.length; i < len; i++){
4933             this.data.insert(index, records[i]);
4934             records[i].join(this);
4935         }
4936         this.fireEvent("add", this, records, index);
4937     },
4938
4939     /**
4940      * Get the index within the cache of the passed Record.
4941      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4942      * @return {Number} The index of the passed Record. Returns -1 if not found.
4943      */
4944     indexOf : function(record){
4945         return this.data.indexOf(record);
4946     },
4947
4948     /**
4949      * Get the index within the cache of the Record with the passed id.
4950      * @param {String} id The id of the Record to find.
4951      * @return {Number} The index of the Record. Returns -1 if not found.
4952      */
4953     indexOfId : function(id){
4954         return this.data.indexOfKey(id);
4955     },
4956
4957     /**
4958      * Get the Record with the specified id.
4959      * @param {String} id The id of the Record to find.
4960      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4961      */
4962     getById : function(id){
4963         return this.data.key(id);
4964     },
4965
4966     /**
4967      * Get the Record at the specified index.
4968      * @param {Number} index The index of the Record to find.
4969      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4970      */
4971     getAt : function(index){
4972         return this.data.itemAt(index);
4973     },
4974
4975     /**
4976      * Returns a range of Records between specified indices.
4977      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4978      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4979      * @return {Roo.data.Record[]} An array of Records
4980      */
4981     getRange : function(start, end){
4982         return this.data.getRange(start, end);
4983     },
4984
4985     // private
4986     storeOptions : function(o){
4987         o = Roo.apply({}, o);
4988         delete o.callback;
4989         delete o.scope;
4990         this.lastOptions = o;
4991     },
4992
4993     /**
4994      * Loads the Record cache from the configured Proxy using the configured Reader.
4995      * <p>
4996      * If using remote paging, then the first load call must specify the <em>start</em>
4997      * and <em>limit</em> properties in the options.params property to establish the initial
4998      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4999      * <p>
5000      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5001      * and this call will return before the new data has been loaded. Perform any post-processing
5002      * in a callback function, or in a "load" event handler.</strong>
5003      * <p>
5004      * @param {Object} options An object containing properties which control loading options:<ul>
5005      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5006      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5007      * passed the following arguments:<ul>
5008      * <li>r : Roo.data.Record[]</li>
5009      * <li>options: Options object from the load call</li>
5010      * <li>success: Boolean success indicator</li></ul></li>
5011      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5012      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5013      * </ul>
5014      */
5015     load : function(options){
5016         options = options || {};
5017         if(this.fireEvent("beforeload", this, options) !== false){
5018             this.storeOptions(options);
5019             var p = Roo.apply(options.params || {}, this.baseParams);
5020             // if meta was not loaded from remote source.. try requesting it.
5021             if (!this.reader.metaFromRemote) {
5022                 p._requestMeta = 1;
5023             }
5024             if(this.sortInfo && this.remoteSort){
5025                 var pn = this.paramNames;
5026                 p[pn["sort"]] = this.sortInfo.field;
5027                 p[pn["dir"]] = this.sortInfo.direction;
5028             }
5029             if (this.multiSort) {
5030                 var pn = this.paramNames;
5031                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5032             }
5033             
5034             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5035         }
5036     },
5037
5038     /**
5039      * Reloads the Record cache from the configured Proxy using the configured Reader and
5040      * the options from the last load operation performed.
5041      * @param {Object} options (optional) An object containing properties which may override the options
5042      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5043      * the most recently used options are reused).
5044      */
5045     reload : function(options){
5046         this.load(Roo.applyIf(options||{}, this.lastOptions));
5047     },
5048
5049     // private
5050     // Called as a callback by the Reader during a load operation.
5051     loadRecords : function(o, options, success){
5052         if(!o || success === false){
5053             if(success !== false){
5054                 this.fireEvent("load", this, [], options);
5055             }
5056             if(options.callback){
5057                 options.callback.call(options.scope || this, [], options, false);
5058             }
5059             return;
5060         }
5061         // if data returned failure - throw an exception.
5062         if (o.success === false) {
5063             // show a message if no listener is registered.
5064             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
5065                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
5066             }
5067             // loadmask wil be hooked into this..
5068             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5069             return;
5070         }
5071         var r = o.records, t = o.totalRecords || r.length;
5072         if(!options || options.add !== true){
5073             if(this.pruneModifiedRecords){
5074                 this.modified = [];
5075             }
5076             for(var i = 0, len = r.length; i < len; i++){
5077                 r[i].join(this);
5078             }
5079             if(this.snapshot){
5080                 this.data = this.snapshot;
5081                 delete this.snapshot;
5082             }
5083             this.data.clear();
5084             this.data.addAll(r);
5085             this.totalLength = t;
5086             this.applySort();
5087             this.fireEvent("datachanged", this);
5088         }else{
5089             this.totalLength = Math.max(t, this.data.length+r.length);
5090             this.add(r);
5091         }
5092         this.fireEvent("load", this, r, options);
5093         if(options.callback){
5094             options.callback.call(options.scope || this, r, options, true);
5095         }
5096     },
5097
5098
5099     /**
5100      * Loads data from a passed data block. A Reader which understands the format of the data
5101      * must have been configured in the constructor.
5102      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5103      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5104      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5105      */
5106     loadData : function(o, append){
5107         var r = this.reader.readRecords(o);
5108         this.loadRecords(r, {add: append}, true);
5109     },
5110
5111     /**
5112      * Gets the number of cached records.
5113      * <p>
5114      * <em>If using paging, this may not be the total size of the dataset. If the data object
5115      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5116      * the data set size</em>
5117      */
5118     getCount : function(){
5119         return this.data.length || 0;
5120     },
5121
5122     /**
5123      * Gets the total number of records in the dataset as returned by the server.
5124      * <p>
5125      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5126      * the dataset size</em>
5127      */
5128     getTotalCount : function(){
5129         return this.totalLength || 0;
5130     },
5131
5132     /**
5133      * Returns the sort state of the Store as an object with two properties:
5134      * <pre><code>
5135  field {String} The name of the field by which the Records are sorted
5136  direction {String} The sort order, "ASC" or "DESC"
5137      * </code></pre>
5138      */
5139     getSortState : function(){
5140         return this.sortInfo;
5141     },
5142
5143     // private
5144     applySort : function(){
5145         if(this.sortInfo && !this.remoteSort){
5146             var s = this.sortInfo, f = s.field;
5147             var st = this.fields.get(f).sortType;
5148             var fn = function(r1, r2){
5149                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5150                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5151             };
5152             this.data.sort(s.direction, fn);
5153             if(this.snapshot && this.snapshot != this.data){
5154                 this.snapshot.sort(s.direction, fn);
5155             }
5156         }
5157     },
5158
5159     /**
5160      * Sets the default sort column and order to be used by the next load operation.
5161      * @param {String} fieldName The name of the field to sort by.
5162      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5163      */
5164     setDefaultSort : function(field, dir){
5165         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5166     },
5167
5168     /**
5169      * Sort the Records.
5170      * If remote sorting is used, the sort is performed on the server, and the cache is
5171      * reloaded. If local sorting is used, the cache is sorted internally.
5172      * @param {String} fieldName The name of the field to sort by.
5173      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5174      */
5175     sort : function(fieldName, dir){
5176         var f = this.fields.get(fieldName);
5177         if(!dir){
5178             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5179             
5180             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5181                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5182             }else{
5183                 dir = f.sortDir;
5184             }
5185         }
5186         this.sortToggle[f.name] = dir;
5187         this.sortInfo = {field: f.name, direction: dir};
5188         if(!this.remoteSort){
5189             this.applySort();
5190             this.fireEvent("datachanged", this);
5191         }else{
5192             this.load(this.lastOptions);
5193         }
5194     },
5195
5196     /**
5197      * Calls the specified function for each of the Records in the cache.
5198      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5199      * Returning <em>false</em> aborts and exits the iteration.
5200      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5201      */
5202     each : function(fn, scope){
5203         this.data.each(fn, scope);
5204     },
5205
5206     /**
5207      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5208      * (e.g., during paging).
5209      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5210      */
5211     getModifiedRecords : function(){
5212         return this.modified;
5213     },
5214
5215     // private
5216     createFilterFn : function(property, value, anyMatch){
5217         if(!value.exec){ // not a regex
5218             value = String(value);
5219             if(value.length == 0){
5220                 return false;
5221             }
5222             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5223         }
5224         return function(r){
5225             return value.test(r.data[property]);
5226         };
5227     },
5228
5229     /**
5230      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5231      * @param {String} property A field on your records
5232      * @param {Number} start The record index to start at (defaults to 0)
5233      * @param {Number} end The last record index to include (defaults to length - 1)
5234      * @return {Number} The sum
5235      */
5236     sum : function(property, start, end){
5237         var rs = this.data.items, v = 0;
5238         start = start || 0;
5239         end = (end || end === 0) ? end : rs.length-1;
5240
5241         for(var i = start; i <= end; i++){
5242             v += (rs[i].data[property] || 0);
5243         }
5244         return v;
5245     },
5246
5247     /**
5248      * Filter the records by a specified property.
5249      * @param {String} field A field on your records
5250      * @param {String/RegExp} value Either a string that the field
5251      * should start with or a RegExp to test against the field
5252      * @param {Boolean} anyMatch True to match any part not just the beginning
5253      */
5254     filter : function(property, value, anyMatch){
5255         var fn = this.createFilterFn(property, value, anyMatch);
5256         return fn ? this.filterBy(fn) : this.clearFilter();
5257     },
5258
5259     /**
5260      * Filter by a function. The specified function will be called with each
5261      * record in this data source. If the function returns true the record is included,
5262      * otherwise it is filtered.
5263      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5264      * @param {Object} scope (optional) The scope of the function (defaults to this)
5265      */
5266     filterBy : function(fn, scope){
5267         this.snapshot = this.snapshot || this.data;
5268         this.data = this.queryBy(fn, scope||this);
5269         this.fireEvent("datachanged", this);
5270     },
5271
5272     /**
5273      * Query the records by a specified property.
5274      * @param {String} field A field on your records
5275      * @param {String/RegExp} value Either a string that the field
5276      * should start with or a RegExp to test against the field
5277      * @param {Boolean} anyMatch True to match any part not just the beginning
5278      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5279      */
5280     query : function(property, value, anyMatch){
5281         var fn = this.createFilterFn(property, value, anyMatch);
5282         return fn ? this.queryBy(fn) : this.data.clone();
5283     },
5284
5285     /**
5286      * Query by a function. The specified function will be called with each
5287      * record in this data source. If the function returns true the record is included
5288      * in the results.
5289      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5290      * @param {Object} scope (optional) The scope of the function (defaults to this)
5291       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5292      **/
5293     queryBy : function(fn, scope){
5294         var data = this.snapshot || this.data;
5295         return data.filterBy(fn, scope||this);
5296     },
5297
5298     /**
5299      * Collects unique values for a particular dataIndex from this store.
5300      * @param {String} dataIndex The property to collect
5301      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5302      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5303      * @return {Array} An array of the unique values
5304      **/
5305     collect : function(dataIndex, allowNull, bypassFilter){
5306         var d = (bypassFilter === true && this.snapshot) ?
5307                 this.snapshot.items : this.data.items;
5308         var v, sv, r = [], l = {};
5309         for(var i = 0, len = d.length; i < len; i++){
5310             v = d[i].data[dataIndex];
5311             sv = String(v);
5312             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5313                 l[sv] = true;
5314                 r[r.length] = v;
5315             }
5316         }
5317         return r;
5318     },
5319
5320     /**
5321      * Revert to a view of the Record cache with no filtering applied.
5322      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5323      */
5324     clearFilter : function(suppressEvent){
5325         if(this.snapshot && this.snapshot != this.data){
5326             this.data = this.snapshot;
5327             delete this.snapshot;
5328             if(suppressEvent !== true){
5329                 this.fireEvent("datachanged", this);
5330             }
5331         }
5332     },
5333
5334     // private
5335     afterEdit : function(record){
5336         if(this.modified.indexOf(record) == -1){
5337             this.modified.push(record);
5338         }
5339         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5340     },
5341     
5342     // private
5343     afterReject : function(record){
5344         this.modified.remove(record);
5345         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5346     },
5347
5348     // private
5349     afterCommit : function(record){
5350         this.modified.remove(record);
5351         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5352     },
5353
5354     /**
5355      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5356      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5357      */
5358     commitChanges : function(){
5359         var m = this.modified.slice(0);
5360         this.modified = [];
5361         for(var i = 0, len = m.length; i < len; i++){
5362             m[i].commit();
5363         }
5364     },
5365
5366     /**
5367      * Cancel outstanding changes on all changed records.
5368      */
5369     rejectChanges : function(){
5370         var m = this.modified.slice(0);
5371         this.modified = [];
5372         for(var i = 0, len = m.length; i < len; i++){
5373             m[i].reject();
5374         }
5375     },
5376
5377     onMetaChange : function(meta, rtype, o){
5378         this.recordType = rtype;
5379         this.fields = rtype.prototype.fields;
5380         delete this.snapshot;
5381         this.sortInfo = meta.sortInfo || this.sortInfo;
5382         this.modified = [];
5383         this.fireEvent('metachange', this, this.reader.meta);
5384     }
5385 });/*
5386  * Based on:
5387  * Ext JS Library 1.1.1
5388  * Copyright(c) 2006-2007, Ext JS, LLC.
5389  *
5390  * Originally Released Under LGPL - original licence link has changed is not relivant.
5391  *
5392  * Fork - LGPL
5393  * <script type="text/javascript">
5394  */
5395
5396 /**
5397  * @class Roo.data.SimpleStore
5398  * @extends Roo.data.Store
5399  * Small helper class to make creating Stores from Array data easier.
5400  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5401  * @cfg {Array} fields An array of field definition objects, or field name strings.
5402  * @cfg {Array} data The multi-dimensional array of data
5403  * @constructor
5404  * @param {Object} config
5405  */
5406 Roo.data.SimpleStore = function(config){
5407     Roo.data.SimpleStore.superclass.constructor.call(this, {
5408         isLocal : true,
5409         reader: new Roo.data.ArrayReader({
5410                 id: config.id
5411             },
5412             Roo.data.Record.create(config.fields)
5413         ),
5414         proxy : new Roo.data.MemoryProxy(config.data)
5415     });
5416     this.load();
5417 };
5418 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5419  * Based on:
5420  * Ext JS Library 1.1.1
5421  * Copyright(c) 2006-2007, Ext JS, LLC.
5422  *
5423  * Originally Released Under LGPL - original licence link has changed is not relivant.
5424  *
5425  * Fork - LGPL
5426  * <script type="text/javascript">
5427  */
5428
5429 /**
5430 /**
5431  * @extends Roo.data.Store
5432  * @class Roo.data.JsonStore
5433  * Small helper class to make creating Stores for JSON data easier. <br/>
5434 <pre><code>
5435 var store = new Roo.data.JsonStore({
5436     url: 'get-images.php',
5437     root: 'images',
5438     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5439 });
5440 </code></pre>
5441  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5442  * JsonReader and HttpProxy (unless inline data is provided).</b>
5443  * @cfg {Array} fields An array of field definition objects, or field name strings.
5444  * @constructor
5445  * @param {Object} config
5446  */
5447 Roo.data.JsonStore = function(c){
5448     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5449         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5450         reader: new Roo.data.JsonReader(c, c.fields)
5451     }));
5452 };
5453 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5454  * Based on:
5455  * Ext JS Library 1.1.1
5456  * Copyright(c) 2006-2007, Ext JS, LLC.
5457  *
5458  * Originally Released Under LGPL - original licence link has changed is not relivant.
5459  *
5460  * Fork - LGPL
5461  * <script type="text/javascript">
5462  */
5463
5464  
5465 Roo.data.Field = function(config){
5466     if(typeof config == "string"){
5467         config = {name: config};
5468     }
5469     Roo.apply(this, config);
5470     
5471     if(!this.type){
5472         this.type = "auto";
5473     }
5474     
5475     var st = Roo.data.SortTypes;
5476     // named sortTypes are supported, here we look them up
5477     if(typeof this.sortType == "string"){
5478         this.sortType = st[this.sortType];
5479     }
5480     
5481     // set default sortType for strings and dates
5482     if(!this.sortType){
5483         switch(this.type){
5484             case "string":
5485                 this.sortType = st.asUCString;
5486                 break;
5487             case "date":
5488                 this.sortType = st.asDate;
5489                 break;
5490             default:
5491                 this.sortType = st.none;
5492         }
5493     }
5494
5495     // define once
5496     var stripRe = /[\$,%]/g;
5497
5498     // prebuilt conversion function for this field, instead of
5499     // switching every time we're reading a value
5500     if(!this.convert){
5501         var cv, dateFormat = this.dateFormat;
5502         switch(this.type){
5503             case "":
5504             case "auto":
5505             case undefined:
5506                 cv = function(v){ return v; };
5507                 break;
5508             case "string":
5509                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5510                 break;
5511             case "int":
5512                 cv = function(v){
5513                     return v !== undefined && v !== null && v !== '' ?
5514                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5515                     };
5516                 break;
5517             case "float":
5518                 cv = function(v){
5519                     return v !== undefined && v !== null && v !== '' ?
5520                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5521                     };
5522                 break;
5523             case "bool":
5524             case "boolean":
5525                 cv = function(v){ return v === true || v === "true" || v == 1; };
5526                 break;
5527             case "date":
5528                 cv = function(v){
5529                     if(!v){
5530                         return '';
5531                     }
5532                     if(v instanceof Date){
5533                         return v;
5534                     }
5535                     if(dateFormat){
5536                         if(dateFormat == "timestamp"){
5537                             return new Date(v*1000);
5538                         }
5539                         return Date.parseDate(v, dateFormat);
5540                     }
5541                     var parsed = Date.parse(v);
5542                     return parsed ? new Date(parsed) : null;
5543                 };
5544              break;
5545             
5546         }
5547         this.convert = cv;
5548     }
5549 };
5550
5551 Roo.data.Field.prototype = {
5552     dateFormat: null,
5553     defaultValue: "",
5554     mapping: null,
5555     sortType : null,
5556     sortDir : "ASC"
5557 };/*
5558  * Based on:
5559  * Ext JS Library 1.1.1
5560  * Copyright(c) 2006-2007, Ext JS, LLC.
5561  *
5562  * Originally Released Under LGPL - original licence link has changed is not relivant.
5563  *
5564  * Fork - LGPL
5565  * <script type="text/javascript">
5566  */
5567  
5568 // Base class for reading structured data from a data source.  This class is intended to be
5569 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5570
5571 /**
5572  * @class Roo.data.DataReader
5573  * Base class for reading structured data from a data source.  This class is intended to be
5574  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5575  */
5576
5577 Roo.data.DataReader = function(meta, recordType){
5578     
5579     this.meta = meta;
5580     
5581     this.recordType = recordType instanceof Array ? 
5582         Roo.data.Record.create(recordType) : recordType;
5583 };
5584
5585 Roo.data.DataReader.prototype = {
5586      /**
5587      * Create an empty record
5588      * @param {Object} data (optional) - overlay some values
5589      * @return {Roo.data.Record} record created.
5590      */
5591     newRow :  function(d) {
5592         var da =  {};
5593         this.recordType.prototype.fields.each(function(c) {
5594             switch( c.type) {
5595                 case 'int' : da[c.name] = 0; break;
5596                 case 'date' : da[c.name] = new Date(); break;
5597                 case 'float' : da[c.name] = 0.0; break;
5598                 case 'boolean' : da[c.name] = false; break;
5599                 default : da[c.name] = ""; break;
5600             }
5601             
5602         });
5603         return new this.recordType(Roo.apply(da, d));
5604     }
5605     
5606 };/*
5607  * Based on:
5608  * Ext JS Library 1.1.1
5609  * Copyright(c) 2006-2007, Ext JS, LLC.
5610  *
5611  * Originally Released Under LGPL - original licence link has changed is not relivant.
5612  *
5613  * Fork - LGPL
5614  * <script type="text/javascript">
5615  */
5616
5617 /**
5618  * @class Roo.data.DataProxy
5619  * @extends Roo.data.Observable
5620  * This class is an abstract base class for implementations which provide retrieval of
5621  * unformatted data objects.<br>
5622  * <p>
5623  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5624  * (of the appropriate type which knows how to parse the data object) to provide a block of
5625  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5626  * <p>
5627  * Custom implementations must implement the load method as described in
5628  * {@link Roo.data.HttpProxy#load}.
5629  */
5630 Roo.data.DataProxy = function(){
5631     this.addEvents({
5632         /**
5633          * @event beforeload
5634          * Fires before a network request is made to retrieve a data object.
5635          * @param {Object} This DataProxy object.
5636          * @param {Object} params The params parameter to the load function.
5637          */
5638         beforeload : true,
5639         /**
5640          * @event load
5641          * Fires before the load method's callback is called.
5642          * @param {Object} This DataProxy object.
5643          * @param {Object} o The data object.
5644          * @param {Object} arg The callback argument object passed to the load function.
5645          */
5646         load : true,
5647         /**
5648          * @event loadexception
5649          * Fires if an Exception occurs during data retrieval.
5650          * @param {Object} This DataProxy object.
5651          * @param {Object} o The data object.
5652          * @param {Object} arg The callback argument object passed to the load function.
5653          * @param {Object} e The Exception.
5654          */
5655         loadexception : true
5656     });
5657     Roo.data.DataProxy.superclass.constructor.call(this);
5658 };
5659
5660 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5661
5662     /**
5663      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5664      */
5665 /*
5666  * Based on:
5667  * Ext JS Library 1.1.1
5668  * Copyright(c) 2006-2007, Ext JS, LLC.
5669  *
5670  * Originally Released Under LGPL - original licence link has changed is not relivant.
5671  *
5672  * Fork - LGPL
5673  * <script type="text/javascript">
5674  */
5675 /**
5676  * @class Roo.data.MemoryProxy
5677  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5678  * to the Reader when its load method is called.
5679  * @constructor
5680  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5681  */
5682 Roo.data.MemoryProxy = function(data){
5683     if (data.data) {
5684         data = data.data;
5685     }
5686     Roo.data.MemoryProxy.superclass.constructor.call(this);
5687     this.data = data;
5688 };
5689
5690 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5691     /**
5692      * Load data from the requested source (in this case an in-memory
5693      * data object passed to the constructor), read the data object into
5694      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5695      * process that block using the passed callback.
5696      * @param {Object} params This parameter is not used by the MemoryProxy class.
5697      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5698      * object into a block of Roo.data.Records.
5699      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5700      * The function must be passed <ul>
5701      * <li>The Record block object</li>
5702      * <li>The "arg" argument from the load function</li>
5703      * <li>A boolean success indicator</li>
5704      * </ul>
5705      * @param {Object} scope The scope in which to call the callback
5706      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5707      */
5708     load : function(params, reader, callback, scope, arg){
5709         params = params || {};
5710         var result;
5711         try {
5712             result = reader.readRecords(this.data);
5713         }catch(e){
5714             this.fireEvent("loadexception", this, arg, null, e);
5715             callback.call(scope, null, arg, false);
5716             return;
5717         }
5718         callback.call(scope, result, arg, true);
5719     },
5720     
5721     // private
5722     update : function(params, records){
5723         
5724     }
5725 });/*
5726  * Based on:
5727  * Ext JS Library 1.1.1
5728  * Copyright(c) 2006-2007, Ext JS, LLC.
5729  *
5730  * Originally Released Under LGPL - original licence link has changed is not relivant.
5731  *
5732  * Fork - LGPL
5733  * <script type="text/javascript">
5734  */
5735 /**
5736  * @class Roo.data.HttpProxy
5737  * @extends Roo.data.DataProxy
5738  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5739  * configured to reference a certain URL.<br><br>
5740  * <p>
5741  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5742  * from which the running page was served.<br><br>
5743  * <p>
5744  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5745  * <p>
5746  * Be aware that to enable the browser to parse an XML document, the server must set
5747  * the Content-Type header in the HTTP response to "text/xml".
5748  * @constructor
5749  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5750  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5751  * will be used to make the request.
5752  */
5753 Roo.data.HttpProxy = function(conn){
5754     Roo.data.HttpProxy.superclass.constructor.call(this);
5755     // is conn a conn config or a real conn?
5756     this.conn = conn;
5757     this.useAjax = !conn || !conn.events;
5758   
5759 };
5760
5761 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5762     // thse are take from connection...
5763     
5764     /**
5765      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5766      */
5767     /**
5768      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5769      * extra parameters to each request made by this object. (defaults to undefined)
5770      */
5771     /**
5772      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5773      *  to each request made by this object. (defaults to undefined)
5774      */
5775     /**
5776      * @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)
5777      */
5778     /**
5779      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5780      */
5781      /**
5782      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5783      * @type Boolean
5784      */
5785   
5786
5787     /**
5788      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5789      * @type Boolean
5790      */
5791     /**
5792      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5793      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5794      * a finer-grained basis than the DataProxy events.
5795      */
5796     getConnection : function(){
5797         return this.useAjax ? Roo.Ajax : this.conn;
5798     },
5799
5800     /**
5801      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5802      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5803      * process that block using the passed callback.
5804      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5805      * for the request to the remote server.
5806      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5807      * object into a block of Roo.data.Records.
5808      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5809      * The function must be passed <ul>
5810      * <li>The Record block object</li>
5811      * <li>The "arg" argument from the load function</li>
5812      * <li>A boolean success indicator</li>
5813      * </ul>
5814      * @param {Object} scope The scope in which to call the callback
5815      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5816      */
5817     load : function(params, reader, callback, scope, arg){
5818         if(this.fireEvent("beforeload", this, params) !== false){
5819             var  o = {
5820                 params : params || {},
5821                 request: {
5822                     callback : callback,
5823                     scope : scope,
5824                     arg : arg
5825                 },
5826                 reader: reader,
5827                 callback : this.loadResponse,
5828                 scope: this
5829             };
5830             if(this.useAjax){
5831                 Roo.applyIf(o, this.conn);
5832                 if(this.activeRequest){
5833                     Roo.Ajax.abort(this.activeRequest);
5834                 }
5835                 this.activeRequest = Roo.Ajax.request(o);
5836             }else{
5837                 this.conn.request(o);
5838             }
5839         }else{
5840             callback.call(scope||this, null, arg, false);
5841         }
5842     },
5843
5844     // private
5845     loadResponse : function(o, success, response){
5846         delete this.activeRequest;
5847         if(!success){
5848             this.fireEvent("loadexception", this, o, response);
5849             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5850             return;
5851         }
5852         var result;
5853         try {
5854             result = o.reader.read(response);
5855         }catch(e){
5856             this.fireEvent("loadexception", this, o, response, e);
5857             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5858             return;
5859         }
5860         
5861         this.fireEvent("load", this, o, o.request.arg);
5862         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5863     },
5864
5865     // private
5866     update : function(dataSet){
5867
5868     },
5869
5870     // private
5871     updateResponse : function(dataSet){
5872
5873     }
5874 });/*
5875  * Based on:
5876  * Ext JS Library 1.1.1
5877  * Copyright(c) 2006-2007, Ext JS, LLC.
5878  *
5879  * Originally Released Under LGPL - original licence link has changed is not relivant.
5880  *
5881  * Fork - LGPL
5882  * <script type="text/javascript">
5883  */
5884
5885 /**
5886  * @class Roo.data.ScriptTagProxy
5887  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5888  * other than the originating domain of the running page.<br><br>
5889  * <p>
5890  * <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
5891  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5892  * <p>
5893  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5894  * source code that is used as the source inside a &lt;script> tag.<br><br>
5895  * <p>
5896  * In order for the browser to process the returned data, the server must wrap the data object
5897  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5898  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5899  * depending on whether the callback name was passed:
5900  * <p>
5901  * <pre><code>
5902 boolean scriptTag = false;
5903 String cb = request.getParameter("callback");
5904 if (cb != null) {
5905     scriptTag = true;
5906     response.setContentType("text/javascript");
5907 } else {
5908     response.setContentType("application/x-json");
5909 }
5910 Writer out = response.getWriter();
5911 if (scriptTag) {
5912     out.write(cb + "(");
5913 }
5914 out.print(dataBlock.toJsonString());
5915 if (scriptTag) {
5916     out.write(");");
5917 }
5918 </pre></code>
5919  *
5920  * @constructor
5921  * @param {Object} config A configuration object.
5922  */
5923 Roo.data.ScriptTagProxy = function(config){
5924     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5925     Roo.apply(this, config);
5926     this.head = document.getElementsByTagName("head")[0];
5927 };
5928
5929 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5930
5931 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5932     /**
5933      * @cfg {String} url The URL from which to request the data object.
5934      */
5935     /**
5936      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5937      */
5938     timeout : 30000,
5939     /**
5940      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5941      * the server the name of the callback function set up by the load call to process the returned data object.
5942      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5943      * javascript output which calls this named function passing the data object as its only parameter.
5944      */
5945     callbackParam : "callback",
5946     /**
5947      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5948      * name to the request.
5949      */
5950     nocache : true,
5951
5952     /**
5953      * Load data from the configured URL, read the data object into
5954      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5955      * process that block using the passed callback.
5956      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5957      * for the request to the remote server.
5958      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5959      * object into a block of Roo.data.Records.
5960      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5961      * The function must be passed <ul>
5962      * <li>The Record block object</li>
5963      * <li>The "arg" argument from the load function</li>
5964      * <li>A boolean success indicator</li>
5965      * </ul>
5966      * @param {Object} scope The scope in which to call the callback
5967      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5968      */
5969     load : function(params, reader, callback, scope, arg){
5970         if(this.fireEvent("beforeload", this, params) !== false){
5971
5972             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5973
5974             var url = this.url;
5975             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5976             if(this.nocache){
5977                 url += "&_dc=" + (new Date().getTime());
5978             }
5979             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5980             var trans = {
5981                 id : transId,
5982                 cb : "stcCallback"+transId,
5983                 scriptId : "stcScript"+transId,
5984                 params : params,
5985                 arg : arg,
5986                 url : url,
5987                 callback : callback,
5988                 scope : scope,
5989                 reader : reader
5990             };
5991             var conn = this;
5992
5993             window[trans.cb] = function(o){
5994                 conn.handleResponse(o, trans);
5995             };
5996
5997             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5998
5999             if(this.autoAbort !== false){
6000                 this.abort();
6001             }
6002
6003             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6004
6005             var script = document.createElement("script");
6006             script.setAttribute("src", url);
6007             script.setAttribute("type", "text/javascript");
6008             script.setAttribute("id", trans.scriptId);
6009             this.head.appendChild(script);
6010
6011             this.trans = trans;
6012         }else{
6013             callback.call(scope||this, null, arg, false);
6014         }
6015     },
6016
6017     // private
6018     isLoading : function(){
6019         return this.trans ? true : false;
6020     },
6021
6022     /**
6023      * Abort the current server request.
6024      */
6025     abort : function(){
6026         if(this.isLoading()){
6027             this.destroyTrans(this.trans);
6028         }
6029     },
6030
6031     // private
6032     destroyTrans : function(trans, isLoaded){
6033         this.head.removeChild(document.getElementById(trans.scriptId));
6034         clearTimeout(trans.timeoutId);
6035         if(isLoaded){
6036             window[trans.cb] = undefined;
6037             try{
6038                 delete window[trans.cb];
6039             }catch(e){}
6040         }else{
6041             // if hasn't been loaded, wait for load to remove it to prevent script error
6042             window[trans.cb] = function(){
6043                 window[trans.cb] = undefined;
6044                 try{
6045                     delete window[trans.cb];
6046                 }catch(e){}
6047             };
6048         }
6049     },
6050
6051     // private
6052     handleResponse : function(o, trans){
6053         this.trans = false;
6054         this.destroyTrans(trans, true);
6055         var result;
6056         try {
6057             result = trans.reader.readRecords(o);
6058         }catch(e){
6059             this.fireEvent("loadexception", this, o, trans.arg, e);
6060             trans.callback.call(trans.scope||window, null, trans.arg, false);
6061             return;
6062         }
6063         this.fireEvent("load", this, o, trans.arg);
6064         trans.callback.call(trans.scope||window, result, trans.arg, true);
6065     },
6066
6067     // private
6068     handleFailure : function(trans){
6069         this.trans = false;
6070         this.destroyTrans(trans, false);
6071         this.fireEvent("loadexception", this, null, trans.arg);
6072         trans.callback.call(trans.scope||window, null, trans.arg, false);
6073     }
6074 });/*
6075  * Based on:
6076  * Ext JS Library 1.1.1
6077  * Copyright(c) 2006-2007, Ext JS, LLC.
6078  *
6079  * Originally Released Under LGPL - original licence link has changed is not relivant.
6080  *
6081  * Fork - LGPL
6082  * <script type="text/javascript">
6083  */
6084
6085 /**
6086  * @class Roo.data.JsonReader
6087  * @extends Roo.data.DataReader
6088  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6089  * based on mappings in a provided Roo.data.Record constructor.
6090  * 
6091  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6092  * in the reply previously. 
6093  * 
6094  * <p>
6095  * Example code:
6096  * <pre><code>
6097 var RecordDef = Roo.data.Record.create([
6098     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6099     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6100 ]);
6101 var myReader = new Roo.data.JsonReader({
6102     totalProperty: "results",    // The property which contains the total dataset size (optional)
6103     root: "rows",                // The property which contains an Array of row objects
6104     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6105 }, RecordDef);
6106 </code></pre>
6107  * <p>
6108  * This would consume a JSON file like this:
6109  * <pre><code>
6110 { 'results': 2, 'rows': [
6111     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6112     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6113 }
6114 </code></pre>
6115  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6116  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6117  * paged from the remote server.
6118  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6119  * @cfg {String} root name of the property which contains the Array of row objects.
6120  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6121  * @constructor
6122  * Create a new JsonReader
6123  * @param {Object} meta Metadata configuration options
6124  * @param {Object} recordType Either an Array of field definition objects,
6125  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6126  */
6127 Roo.data.JsonReader = function(meta, recordType){
6128     
6129     meta = meta || {};
6130     // set some defaults:
6131     Roo.applyIf(meta, {
6132         totalProperty: 'total',
6133         successProperty : 'success',
6134         root : 'data',
6135         id : 'id'
6136     });
6137     
6138     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6139 };
6140 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6141     
6142     /**
6143      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6144      * Used by Store query builder to append _requestMeta to params.
6145      * 
6146      */
6147     metaFromRemote : false,
6148     /**
6149      * This method is only used by a DataProxy which has retrieved data from a remote server.
6150      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6151      * @return {Object} data A data block which is used by an Roo.data.Store object as
6152      * a cache of Roo.data.Records.
6153      */
6154     read : function(response){
6155         var json = response.responseText;
6156        
6157         var o = /* eval:var:o */ eval("("+json+")");
6158         if(!o) {
6159             throw {message: "JsonReader.read: Json object not found"};
6160         }
6161         
6162         if(o.metaData){
6163             
6164             delete this.ef;
6165             this.metaFromRemote = true;
6166             this.meta = o.metaData;
6167             this.recordType = Roo.data.Record.create(o.metaData.fields);
6168             this.onMetaChange(this.meta, this.recordType, o);
6169         }
6170         return this.readRecords(o);
6171     },
6172
6173     // private function a store will implement
6174     onMetaChange : function(meta, recordType, o){
6175
6176     },
6177
6178     /**
6179          * @ignore
6180          */
6181     simpleAccess: function(obj, subsc) {
6182         return obj[subsc];
6183     },
6184
6185         /**
6186          * @ignore
6187          */
6188     getJsonAccessor: function(){
6189         var re = /[\[\.]/;
6190         return function(expr) {
6191             try {
6192                 return(re.test(expr))
6193                     ? new Function("obj", "return obj." + expr)
6194                     : function(obj){
6195                         return obj[expr];
6196                     };
6197             } catch(e){}
6198             return Roo.emptyFn;
6199         };
6200     }(),
6201
6202     /**
6203      * Create a data block containing Roo.data.Records from an XML document.
6204      * @param {Object} o An object which contains an Array of row objects in the property specified
6205      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6206      * which contains the total size of the dataset.
6207      * @return {Object} data A data block which is used by an Roo.data.Store object as
6208      * a cache of Roo.data.Records.
6209      */
6210     readRecords : function(o){
6211         /**
6212          * After any data loads, the raw JSON data is available for further custom processing.
6213          * @type Object
6214          */
6215         this.jsonData = o;
6216         var s = this.meta, Record = this.recordType,
6217             f = Record.prototype.fields, fi = f.items, fl = f.length;
6218
6219 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6220         if (!this.ef) {
6221             if(s.totalProperty) {
6222                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6223                 }
6224                 if(s.successProperty) {
6225                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6226                 }
6227                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6228                 if (s.id) {
6229                         var g = this.getJsonAccessor(s.id);
6230                         this.getId = function(rec) {
6231                                 var r = g(rec);
6232                                 return (r === undefined || r === "") ? null : r;
6233                         };
6234                 } else {
6235                         this.getId = function(){return null;};
6236                 }
6237             this.ef = [];
6238             for(var jj = 0; jj < fl; jj++){
6239                 f = fi[jj];
6240                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6241                 this.ef[jj] = this.getJsonAccessor(map);
6242             }
6243         }
6244
6245         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6246         if(s.totalProperty){
6247             var vt = parseInt(this.getTotal(o), 10);
6248             if(!isNaN(vt)){
6249                 totalRecords = vt;
6250             }
6251         }
6252         if(s.successProperty){
6253             var vs = this.getSuccess(o);
6254             if(vs === false || vs === 'false'){
6255                 success = false;
6256             }
6257         }
6258         var records = [];
6259             for(var i = 0; i < c; i++){
6260                     var n = root[i];
6261                 var values = {};
6262                 var id = this.getId(n);
6263                 for(var j = 0; j < fl; j++){
6264                     f = fi[j];
6265                 var v = this.ef[j](n);
6266                 if (!f.convert) {
6267                     Roo.log('missing convert for ' + f.name);
6268                     Roo.log(f);
6269                     continue;
6270                 }
6271                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6272                 }
6273                 var record = new Record(values, id);
6274                 record.json = n;
6275                 records[i] = record;
6276             }
6277             return {
6278                 success : success,
6279                 records : records,
6280                 totalRecords : totalRecords
6281             };
6282     }
6283 });/*
6284  * Based on:
6285  * Ext JS Library 1.1.1
6286  * Copyright(c) 2006-2007, Ext JS, LLC.
6287  *
6288  * Originally Released Under LGPL - original licence link has changed is not relivant.
6289  *
6290  * Fork - LGPL
6291  * <script type="text/javascript">
6292  */
6293
6294 /**
6295  * @class Roo.data.XmlReader
6296  * @extends Roo.data.DataReader
6297  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6298  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6299  * <p>
6300  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6301  * header in the HTTP response must be set to "text/xml".</em>
6302  * <p>
6303  * Example code:
6304  * <pre><code>
6305 var RecordDef = Roo.data.Record.create([
6306    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6307    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6308 ]);
6309 var myReader = new Roo.data.XmlReader({
6310    totalRecords: "results", // The element which contains the total dataset size (optional)
6311    record: "row",           // The repeated element which contains row information
6312    id: "id"                 // The element within the row that provides an ID for the record (optional)
6313 }, RecordDef);
6314 </code></pre>
6315  * <p>
6316  * This would consume an XML file like this:
6317  * <pre><code>
6318 &lt;?xml?>
6319 &lt;dataset>
6320  &lt;results>2&lt;/results>
6321  &lt;row>
6322    &lt;id>1&lt;/id>
6323    &lt;name>Bill&lt;/name>
6324    &lt;occupation>Gardener&lt;/occupation>
6325  &lt;/row>
6326  &lt;row>
6327    &lt;id>2&lt;/id>
6328    &lt;name>Ben&lt;/name>
6329    &lt;occupation>Horticulturalist&lt;/occupation>
6330  &lt;/row>
6331 &lt;/dataset>
6332 </code></pre>
6333  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6334  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6335  * paged from the remote server.
6336  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6337  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6338  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6339  * a record identifier value.
6340  * @constructor
6341  * Create a new XmlReader
6342  * @param {Object} meta Metadata configuration options
6343  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6344  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6345  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6346  */
6347 Roo.data.XmlReader = function(meta, recordType){
6348     meta = meta || {};
6349     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6350 };
6351 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6352     /**
6353      * This method is only used by a DataProxy which has retrieved data from a remote server.
6354          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6355          * to contain a method called 'responseXML' that returns an XML document object.
6356      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6357      * a cache of Roo.data.Records.
6358      */
6359     read : function(response){
6360         var doc = response.responseXML;
6361         if(!doc) {
6362             throw {message: "XmlReader.read: XML Document not available"};
6363         }
6364         return this.readRecords(doc);
6365     },
6366
6367     /**
6368      * Create a data block containing Roo.data.Records from an XML document.
6369          * @param {Object} doc A parsed XML document.
6370      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6371      * a cache of Roo.data.Records.
6372      */
6373     readRecords : function(doc){
6374         /**
6375          * After any data loads/reads, the raw XML Document is available for further custom processing.
6376          * @type XMLDocument
6377          */
6378         this.xmlData = doc;
6379         var root = doc.documentElement || doc;
6380         var q = Roo.DomQuery;
6381         var recordType = this.recordType, fields = recordType.prototype.fields;
6382         var sid = this.meta.id;
6383         var totalRecords = 0, success = true;
6384         if(this.meta.totalRecords){
6385             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6386         }
6387         
6388         if(this.meta.success){
6389             var sv = q.selectValue(this.meta.success, root, true);
6390             success = sv !== false && sv !== 'false';
6391         }
6392         var records = [];
6393         var ns = q.select(this.meta.record, root);
6394         for(var i = 0, len = ns.length; i < len; i++) {
6395                 var n = ns[i];
6396                 var values = {};
6397                 var id = sid ? q.selectValue(sid, n) : undefined;
6398                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6399                     var f = fields.items[j];
6400                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6401                     v = f.convert(v);
6402                     values[f.name] = v;
6403                 }
6404                 var record = new recordType(values, id);
6405                 record.node = n;
6406                 records[records.length] = record;
6407             }
6408
6409             return {
6410                 success : success,
6411                 records : records,
6412                 totalRecords : totalRecords || records.length
6413             };
6414     }
6415 });/*
6416  * Based on:
6417  * Ext JS Library 1.1.1
6418  * Copyright(c) 2006-2007, Ext JS, LLC.
6419  *
6420  * Originally Released Under LGPL - original licence link has changed is not relivant.
6421  *
6422  * Fork - LGPL
6423  * <script type="text/javascript">
6424  */
6425
6426 /**
6427  * @class Roo.data.ArrayReader
6428  * @extends Roo.data.DataReader
6429  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6430  * Each element of that Array represents a row of data fields. The
6431  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6432  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6433  * <p>
6434  * Example code:.
6435  * <pre><code>
6436 var RecordDef = Roo.data.Record.create([
6437     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6438     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6439 ]);
6440 var myReader = new Roo.data.ArrayReader({
6441     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6442 }, RecordDef);
6443 </code></pre>
6444  * <p>
6445  * This would consume an Array like this:
6446  * <pre><code>
6447 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6448   </code></pre>
6449  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6450  * @constructor
6451  * Create a new JsonReader
6452  * @param {Object} meta Metadata configuration options.
6453  * @param {Object} recordType Either an Array of field definition objects
6454  * as specified to {@link Roo.data.Record#create},
6455  * or an {@link Roo.data.Record} object
6456  * created using {@link Roo.data.Record#create}.
6457  */
6458 Roo.data.ArrayReader = function(meta, recordType){
6459     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6460 };
6461
6462 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6463     /**
6464      * Create a data block containing Roo.data.Records from an XML document.
6465      * @param {Object} o An Array of row objects which represents the dataset.
6466      * @return {Object} data A data block which is used by an Roo.data.Store object as
6467      * a cache of Roo.data.Records.
6468      */
6469     readRecords : function(o){
6470         var sid = this.meta ? this.meta.id : null;
6471         var recordType = this.recordType, fields = recordType.prototype.fields;
6472         var records = [];
6473         var root = o;
6474             for(var i = 0; i < root.length; i++){
6475                     var n = root[i];
6476                 var values = {};
6477                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6478                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6479                 var f = fields.items[j];
6480                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6481                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6482                 v = f.convert(v);
6483                 values[f.name] = v;
6484             }
6485                 var record = new recordType(values, id);
6486                 record.json = n;
6487                 records[records.length] = record;
6488             }
6489             return {
6490                 records : records,
6491                 totalRecords : records.length
6492             };
6493     }
6494 });/*
6495  * Based on:
6496  * Ext JS Library 1.1.1
6497  * Copyright(c) 2006-2007, Ext JS, LLC.
6498  *
6499  * Originally Released Under LGPL - original licence link has changed is not relivant.
6500  *
6501  * Fork - LGPL
6502  * <script type="text/javascript">
6503  */
6504
6505
6506 /**
6507  * @class Roo.data.Tree
6508  * @extends Roo.util.Observable
6509  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6510  * in the tree have most standard DOM functionality.
6511  * @constructor
6512  * @param {Node} root (optional) The root node
6513  */
6514 Roo.data.Tree = function(root){
6515    this.nodeHash = {};
6516    /**
6517     * The root node for this tree
6518     * @type Node
6519     */
6520    this.root = null;
6521    if(root){
6522        this.setRootNode(root);
6523    }
6524    this.addEvents({
6525        /**
6526         * @event append
6527         * Fires when a new child node is appended to a node in this tree.
6528         * @param {Tree} tree The owner tree
6529         * @param {Node} parent The parent node
6530         * @param {Node} node The newly appended node
6531         * @param {Number} index The index of the newly appended node
6532         */
6533        "append" : true,
6534        /**
6535         * @event remove
6536         * Fires when a child node is removed from a node in this tree.
6537         * @param {Tree} tree The owner tree
6538         * @param {Node} parent The parent node
6539         * @param {Node} node The child node removed
6540         */
6541        "remove" : true,
6542        /**
6543         * @event move
6544         * Fires when a node is moved to a new location in the tree
6545         * @param {Tree} tree The owner tree
6546         * @param {Node} node The node moved
6547         * @param {Node} oldParent The old parent of this node
6548         * @param {Node} newParent The new parent of this node
6549         * @param {Number} index The index it was moved to
6550         */
6551        "move" : true,
6552        /**
6553         * @event insert
6554         * Fires when a new child node is inserted in a node in this tree.
6555         * @param {Tree} tree The owner tree
6556         * @param {Node} parent The parent node
6557         * @param {Node} node The child node inserted
6558         * @param {Node} refNode The child node the node was inserted before
6559         */
6560        "insert" : true,
6561        /**
6562         * @event beforeappend
6563         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6564         * @param {Tree} tree The owner tree
6565         * @param {Node} parent The parent node
6566         * @param {Node} node The child node to be appended
6567         */
6568        "beforeappend" : true,
6569        /**
6570         * @event beforeremove
6571         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6572         * @param {Tree} tree The owner tree
6573         * @param {Node} parent The parent node
6574         * @param {Node} node The child node to be removed
6575         */
6576        "beforeremove" : true,
6577        /**
6578         * @event beforemove
6579         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6580         * @param {Tree} tree The owner tree
6581         * @param {Node} node The node being moved
6582         * @param {Node} oldParent The parent of the node
6583         * @param {Node} newParent The new parent the node is moving to
6584         * @param {Number} index The index it is being moved to
6585         */
6586        "beforemove" : true,
6587        /**
6588         * @event beforeinsert
6589         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6590         * @param {Tree} tree The owner tree
6591         * @param {Node} parent The parent node
6592         * @param {Node} node The child node to be inserted
6593         * @param {Node} refNode The child node the node is being inserted before
6594         */
6595        "beforeinsert" : true
6596    });
6597
6598     Roo.data.Tree.superclass.constructor.call(this);
6599 };
6600
6601 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6602     pathSeparator: "/",
6603
6604     proxyNodeEvent : function(){
6605         return this.fireEvent.apply(this, arguments);
6606     },
6607
6608     /**
6609      * Returns the root node for this tree.
6610      * @return {Node}
6611      */
6612     getRootNode : function(){
6613         return this.root;
6614     },
6615
6616     /**
6617      * Sets the root node for this tree.
6618      * @param {Node} node
6619      * @return {Node}
6620      */
6621     setRootNode : function(node){
6622         this.root = node;
6623         node.ownerTree = this;
6624         node.isRoot = true;
6625         this.registerNode(node);
6626         return node;
6627     },
6628
6629     /**
6630      * Gets a node in this tree by its id.
6631      * @param {String} id
6632      * @return {Node}
6633      */
6634     getNodeById : function(id){
6635         return this.nodeHash[id];
6636     },
6637
6638     registerNode : function(node){
6639         this.nodeHash[node.id] = node;
6640     },
6641
6642     unregisterNode : function(node){
6643         delete this.nodeHash[node.id];
6644     },
6645
6646     toString : function(){
6647         return "[Tree"+(this.id?" "+this.id:"")+"]";
6648     }
6649 });
6650
6651 /**
6652  * @class Roo.data.Node
6653  * @extends Roo.util.Observable
6654  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6655  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6656  * @constructor
6657  * @param {Object} attributes The attributes/config for the node
6658  */
6659 Roo.data.Node = function(attributes){
6660     /**
6661      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6662      * @type {Object}
6663      */
6664     this.attributes = attributes || {};
6665     this.leaf = this.attributes.leaf;
6666     /**
6667      * The node id. @type String
6668      */
6669     this.id = this.attributes.id;
6670     if(!this.id){
6671         this.id = Roo.id(null, "ynode-");
6672         this.attributes.id = this.id;
6673     }
6674      
6675     
6676     /**
6677      * All child nodes of this node. @type Array
6678      */
6679     this.childNodes = [];
6680     if(!this.childNodes.indexOf){ // indexOf is a must
6681         this.childNodes.indexOf = function(o){
6682             for(var i = 0, len = this.length; i < len; i++){
6683                 if(this[i] == o) {
6684                     return i;
6685                 }
6686             }
6687             return -1;
6688         };
6689     }
6690     /**
6691      * The parent node for this node. @type Node
6692      */
6693     this.parentNode = null;
6694     /**
6695      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6696      */
6697     this.firstChild = null;
6698     /**
6699      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6700      */
6701     this.lastChild = null;
6702     /**
6703      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6704      */
6705     this.previousSibling = null;
6706     /**
6707      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6708      */
6709     this.nextSibling = null;
6710
6711     this.addEvents({
6712        /**
6713         * @event append
6714         * Fires when a new child node is appended
6715         * @param {Tree} tree The owner tree
6716         * @param {Node} this This node
6717         * @param {Node} node The newly appended node
6718         * @param {Number} index The index of the newly appended node
6719         */
6720        "append" : true,
6721        /**
6722         * @event remove
6723         * Fires when a child node is removed
6724         * @param {Tree} tree The owner tree
6725         * @param {Node} this This node
6726         * @param {Node} node The removed node
6727         */
6728        "remove" : true,
6729        /**
6730         * @event move
6731         * Fires when this node is moved to a new location in the tree
6732         * @param {Tree} tree The owner tree
6733         * @param {Node} this This node
6734         * @param {Node} oldParent The old parent of this node
6735         * @param {Node} newParent The new parent of this node
6736         * @param {Number} index The index it was moved to
6737         */
6738        "move" : true,
6739        /**
6740         * @event insert
6741         * Fires when a new child node is inserted.
6742         * @param {Tree} tree The owner tree
6743         * @param {Node} this This node
6744         * @param {Node} node The child node inserted
6745         * @param {Node} refNode The child node the node was inserted before
6746         */
6747        "insert" : true,
6748        /**
6749         * @event beforeappend
6750         * Fires before a new child is appended, return false to cancel the append.
6751         * @param {Tree} tree The owner tree
6752         * @param {Node} this This node
6753         * @param {Node} node The child node to be appended
6754         */
6755        "beforeappend" : true,
6756        /**
6757         * @event beforeremove
6758         * Fires before a child is removed, return false to cancel the remove.
6759         * @param {Tree} tree The owner tree
6760         * @param {Node} this This node
6761         * @param {Node} node The child node to be removed
6762         */
6763        "beforeremove" : true,
6764        /**
6765         * @event beforemove
6766         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6767         * @param {Tree} tree The owner tree
6768         * @param {Node} this This node
6769         * @param {Node} oldParent The parent of this node
6770         * @param {Node} newParent The new parent this node is moving to
6771         * @param {Number} index The index it is being moved to
6772         */
6773        "beforemove" : true,
6774        /**
6775         * @event beforeinsert
6776         * Fires before a new child is inserted, return false to cancel the insert.
6777         * @param {Tree} tree The owner tree
6778         * @param {Node} this This node
6779         * @param {Node} node The child node to be inserted
6780         * @param {Node} refNode The child node the node is being inserted before
6781         */
6782        "beforeinsert" : true
6783    });
6784     this.listeners = this.attributes.listeners;
6785     Roo.data.Node.superclass.constructor.call(this);
6786 };
6787
6788 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6789     fireEvent : function(evtName){
6790         // first do standard event for this node
6791         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6792             return false;
6793         }
6794         // then bubble it up to the tree if the event wasn't cancelled
6795         var ot = this.getOwnerTree();
6796         if(ot){
6797             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6798                 return false;
6799             }
6800         }
6801         return true;
6802     },
6803
6804     /**
6805      * Returns true if this node is a leaf
6806      * @return {Boolean}
6807      */
6808     isLeaf : function(){
6809         return this.leaf === true;
6810     },
6811
6812     // private
6813     setFirstChild : function(node){
6814         this.firstChild = node;
6815     },
6816
6817     //private
6818     setLastChild : function(node){
6819         this.lastChild = node;
6820     },
6821
6822
6823     /**
6824      * Returns true if this node is the last child of its parent
6825      * @return {Boolean}
6826      */
6827     isLast : function(){
6828        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6829     },
6830
6831     /**
6832      * Returns true if this node is the first child of its parent
6833      * @return {Boolean}
6834      */
6835     isFirst : function(){
6836        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6837     },
6838
6839     hasChildNodes : function(){
6840         return !this.isLeaf() && this.childNodes.length > 0;
6841     },
6842
6843     /**
6844      * Insert node(s) as the last child node of this node.
6845      * @param {Node/Array} node The node or Array of nodes to append
6846      * @return {Node} The appended node if single append, or null if an array was passed
6847      */
6848     appendChild : function(node){
6849         var multi = false;
6850         if(node instanceof Array){
6851             multi = node;
6852         }else if(arguments.length > 1){
6853             multi = arguments;
6854         }
6855         // if passed an array or multiple args do them one by one
6856         if(multi){
6857             for(var i = 0, len = multi.length; i < len; i++) {
6858                 this.appendChild(multi[i]);
6859             }
6860         }else{
6861             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6862                 return false;
6863             }
6864             var index = this.childNodes.length;
6865             var oldParent = node.parentNode;
6866             // it's a move, make sure we move it cleanly
6867             if(oldParent){
6868                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6869                     return false;
6870                 }
6871                 oldParent.removeChild(node);
6872             }
6873             index = this.childNodes.length;
6874             if(index == 0){
6875                 this.setFirstChild(node);
6876             }
6877             this.childNodes.push(node);
6878             node.parentNode = this;
6879             var ps = this.childNodes[index-1];
6880             if(ps){
6881                 node.previousSibling = ps;
6882                 ps.nextSibling = node;
6883             }else{
6884                 node.previousSibling = null;
6885             }
6886             node.nextSibling = null;
6887             this.setLastChild(node);
6888             node.setOwnerTree(this.getOwnerTree());
6889             this.fireEvent("append", this.ownerTree, this, node, index);
6890             if(oldParent){
6891                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6892             }
6893             return node;
6894         }
6895     },
6896
6897     /**
6898      * Removes a child node from this node.
6899      * @param {Node} node The node to remove
6900      * @return {Node} The removed node
6901      */
6902     removeChild : function(node){
6903         var index = this.childNodes.indexOf(node);
6904         if(index == -1){
6905             return false;
6906         }
6907         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6908             return false;
6909         }
6910
6911         // remove it from childNodes collection
6912         this.childNodes.splice(index, 1);
6913
6914         // update siblings
6915         if(node.previousSibling){
6916             node.previousSibling.nextSibling = node.nextSibling;
6917         }
6918         if(node.nextSibling){
6919             node.nextSibling.previousSibling = node.previousSibling;
6920         }
6921
6922         // update child refs
6923         if(this.firstChild == node){
6924             this.setFirstChild(node.nextSibling);
6925         }
6926         if(this.lastChild == node){
6927             this.setLastChild(node.previousSibling);
6928         }
6929
6930         node.setOwnerTree(null);
6931         // clear any references from the node
6932         node.parentNode = null;
6933         node.previousSibling = null;
6934         node.nextSibling = null;
6935         this.fireEvent("remove", this.ownerTree, this, node);
6936         return node;
6937     },
6938
6939     /**
6940      * Inserts the first node before the second node in this nodes childNodes collection.
6941      * @param {Node} node The node to insert
6942      * @param {Node} refNode The node to insert before (if null the node is appended)
6943      * @return {Node} The inserted node
6944      */
6945     insertBefore : function(node, refNode){
6946         if(!refNode){ // like standard Dom, refNode can be null for append
6947             return this.appendChild(node);
6948         }
6949         // nothing to do
6950         if(node == refNode){
6951             return false;
6952         }
6953
6954         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6955             return false;
6956         }
6957         var index = this.childNodes.indexOf(refNode);
6958         var oldParent = node.parentNode;
6959         var refIndex = index;
6960
6961         // when moving internally, indexes will change after remove
6962         if(oldParent == this && this.childNodes.indexOf(node) < index){
6963             refIndex--;
6964         }
6965
6966         // it's a move, make sure we move it cleanly
6967         if(oldParent){
6968             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6969                 return false;
6970             }
6971             oldParent.removeChild(node);
6972         }
6973         if(refIndex == 0){
6974             this.setFirstChild(node);
6975         }
6976         this.childNodes.splice(refIndex, 0, node);
6977         node.parentNode = this;
6978         var ps = this.childNodes[refIndex-1];
6979         if(ps){
6980             node.previousSibling = ps;
6981             ps.nextSibling = node;
6982         }else{
6983             node.previousSibling = null;
6984         }
6985         node.nextSibling = refNode;
6986         refNode.previousSibling = node;
6987         node.setOwnerTree(this.getOwnerTree());
6988         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6989         if(oldParent){
6990             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6991         }
6992         return node;
6993     },
6994
6995     /**
6996      * Returns the child node at the specified index.
6997      * @param {Number} index
6998      * @return {Node}
6999      */
7000     item : function(index){
7001         return this.childNodes[index];
7002     },
7003
7004     /**
7005      * Replaces one child node in this node with another.
7006      * @param {Node} newChild The replacement node
7007      * @param {Node} oldChild The node to replace
7008      * @return {Node} The replaced node
7009      */
7010     replaceChild : function(newChild, oldChild){
7011         this.insertBefore(newChild, oldChild);
7012         this.removeChild(oldChild);
7013         return oldChild;
7014     },
7015
7016     /**
7017      * Returns the index of a child node
7018      * @param {Node} node
7019      * @return {Number} The index of the node or -1 if it was not found
7020      */
7021     indexOf : function(child){
7022         return this.childNodes.indexOf(child);
7023     },
7024
7025     /**
7026      * Returns the tree this node is in.
7027      * @return {Tree}
7028      */
7029     getOwnerTree : function(){
7030         // if it doesn't have one, look for one
7031         if(!this.ownerTree){
7032             var p = this;
7033             while(p){
7034                 if(p.ownerTree){
7035                     this.ownerTree = p.ownerTree;
7036                     break;
7037                 }
7038                 p = p.parentNode;
7039             }
7040         }
7041         return this.ownerTree;
7042     },
7043
7044     /**
7045      * Returns depth of this node (the root node has a depth of 0)
7046      * @return {Number}
7047      */
7048     getDepth : function(){
7049         var depth = 0;
7050         var p = this;
7051         while(p.parentNode){
7052             ++depth;
7053             p = p.parentNode;
7054         }
7055         return depth;
7056     },
7057
7058     // private
7059     setOwnerTree : function(tree){
7060         // if it's move, we need to update everyone
7061         if(tree != this.ownerTree){
7062             if(this.ownerTree){
7063                 this.ownerTree.unregisterNode(this);
7064             }
7065             this.ownerTree = tree;
7066             var cs = this.childNodes;
7067             for(var i = 0, len = cs.length; i < len; i++) {
7068                 cs[i].setOwnerTree(tree);
7069             }
7070             if(tree){
7071                 tree.registerNode(this);
7072             }
7073         }
7074     },
7075
7076     /**
7077      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7078      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7079      * @return {String} The path
7080      */
7081     getPath : function(attr){
7082         attr = attr || "id";
7083         var p = this.parentNode;
7084         var b = [this.attributes[attr]];
7085         while(p){
7086             b.unshift(p.attributes[attr]);
7087             p = p.parentNode;
7088         }
7089         var sep = this.getOwnerTree().pathSeparator;
7090         return sep + b.join(sep);
7091     },
7092
7093     /**
7094      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7095      * function call will be the scope provided or the current node. The arguments to the function
7096      * will be the args provided or the current node. If the function returns false at any point,
7097      * the bubble is stopped.
7098      * @param {Function} fn The function to call
7099      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7100      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7101      */
7102     bubble : function(fn, scope, args){
7103         var p = this;
7104         while(p){
7105             if(fn.call(scope || p, args || p) === false){
7106                 break;
7107             }
7108             p = p.parentNode;
7109         }
7110     },
7111
7112     /**
7113      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7114      * function call will be the scope provided or the current node. The arguments to the function
7115      * will be the args provided or the current node. If the function returns false at any point,
7116      * the cascade is stopped on that branch.
7117      * @param {Function} fn The function to call
7118      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7119      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7120      */
7121     cascade : function(fn, scope, args){
7122         if(fn.call(scope || this, args || this) !== false){
7123             var cs = this.childNodes;
7124             for(var i = 0, len = cs.length; i < len; i++) {
7125                 cs[i].cascade(fn, scope, args);
7126             }
7127         }
7128     },
7129
7130     /**
7131      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7132      * function call will be the scope provided or the current node. The arguments to the function
7133      * will be the args provided or the current node. If the function returns false at any point,
7134      * the iteration stops.
7135      * @param {Function} fn The function to call
7136      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7137      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7138      */
7139     eachChild : function(fn, scope, args){
7140         var cs = this.childNodes;
7141         for(var i = 0, len = cs.length; i < len; i++) {
7142                 if(fn.call(scope || this, args || cs[i]) === false){
7143                     break;
7144                 }
7145         }
7146     },
7147
7148     /**
7149      * Finds the first child that has the attribute with the specified value.
7150      * @param {String} attribute The attribute name
7151      * @param {Mixed} value The value to search for
7152      * @return {Node} The found child or null if none was found
7153      */
7154     findChild : function(attribute, value){
7155         var cs = this.childNodes;
7156         for(var i = 0, len = cs.length; i < len; i++) {
7157                 if(cs[i].attributes[attribute] == value){
7158                     return cs[i];
7159                 }
7160         }
7161         return null;
7162     },
7163
7164     /**
7165      * Finds the first child by a custom function. The child matches if the function passed
7166      * returns true.
7167      * @param {Function} fn
7168      * @param {Object} scope (optional)
7169      * @return {Node} The found child or null if none was found
7170      */
7171     findChildBy : function(fn, scope){
7172         var cs = this.childNodes;
7173         for(var i = 0, len = cs.length; i < len; i++) {
7174                 if(fn.call(scope||cs[i], cs[i]) === true){
7175                     return cs[i];
7176                 }
7177         }
7178         return null;
7179     },
7180
7181     /**
7182      * Sorts this nodes children using the supplied sort function
7183      * @param {Function} fn
7184      * @param {Object} scope (optional)
7185      */
7186     sort : function(fn, scope){
7187         var cs = this.childNodes;
7188         var len = cs.length;
7189         if(len > 0){
7190             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7191             cs.sort(sortFn);
7192             for(var i = 0; i < len; i++){
7193                 var n = cs[i];
7194                 n.previousSibling = cs[i-1];
7195                 n.nextSibling = cs[i+1];
7196                 if(i == 0){
7197                     this.setFirstChild(n);
7198                 }
7199                 if(i == len-1){
7200                     this.setLastChild(n);
7201                 }
7202             }
7203         }
7204     },
7205
7206     /**
7207      * Returns true if this node is an ancestor (at any point) of the passed node.
7208      * @param {Node} node
7209      * @return {Boolean}
7210      */
7211     contains : function(node){
7212         return node.isAncestor(this);
7213     },
7214
7215     /**
7216      * Returns true if the passed node is an ancestor (at any point) of this node.
7217      * @param {Node} node
7218      * @return {Boolean}
7219      */
7220     isAncestor : function(node){
7221         var p = this.parentNode;
7222         while(p){
7223             if(p == node){
7224                 return true;
7225             }
7226             p = p.parentNode;
7227         }
7228         return false;
7229     },
7230
7231     toString : function(){
7232         return "[Node"+(this.id?" "+this.id:"")+"]";
7233     }
7234 });/*
7235  * Based on:
7236  * Ext JS Library 1.1.1
7237  * Copyright(c) 2006-2007, Ext JS, LLC.
7238  *
7239  * Originally Released Under LGPL - original licence link has changed is not relivant.
7240  *
7241  * Fork - LGPL
7242  * <script type="text/javascript">
7243  */
7244  
7245
7246 /**
7247  * @class Roo.ComponentMgr
7248  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7249  * @singleton
7250  */
7251 Roo.ComponentMgr = function(){
7252     var all = new Roo.util.MixedCollection();
7253
7254     return {
7255         /**
7256          * Registers a component.
7257          * @param {Roo.Component} c The component
7258          */
7259         register : function(c){
7260             all.add(c);
7261         },
7262
7263         /**
7264          * Unregisters a component.
7265          * @param {Roo.Component} c The component
7266          */
7267         unregister : function(c){
7268             all.remove(c);
7269         },
7270
7271         /**
7272          * Returns a component by id
7273          * @param {String} id The component id
7274          */
7275         get : function(id){
7276             return all.get(id);
7277         },
7278
7279         /**
7280          * Registers a function that will be called when a specified component is added to ComponentMgr
7281          * @param {String} id The component id
7282          * @param {Funtction} fn The callback function
7283          * @param {Object} scope The scope of the callback
7284          */
7285         onAvailable : function(id, fn, scope){
7286             all.on("add", function(index, o){
7287                 if(o.id == id){
7288                     fn.call(scope || o, o);
7289                     all.un("add", fn, scope);
7290                 }
7291             });
7292         }
7293     };
7294 }();/*
7295  * Based on:
7296  * Ext JS Library 1.1.1
7297  * Copyright(c) 2006-2007, Ext JS, LLC.
7298  *
7299  * Originally Released Under LGPL - original licence link has changed is not relivant.
7300  *
7301  * Fork - LGPL
7302  * <script type="text/javascript">
7303  */
7304  
7305 /**
7306  * @class Roo.Component
7307  * @extends Roo.util.Observable
7308  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7309  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7310  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7311  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7312  * All visual components (widgets) that require rendering into a layout should subclass Component.
7313  * @constructor
7314  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7315  * 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
7316  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7317  */
7318 Roo.Component = function(config){
7319     config = config || {};
7320     if(config.tagName || config.dom || typeof config == "string"){ // element object
7321         config = {el: config, id: config.id || config};
7322     }
7323     this.initialConfig = config;
7324
7325     Roo.apply(this, config);
7326     this.addEvents({
7327         /**
7328          * @event disable
7329          * Fires after the component is disabled.
7330              * @param {Roo.Component} this
7331              */
7332         disable : true,
7333         /**
7334          * @event enable
7335          * Fires after the component is enabled.
7336              * @param {Roo.Component} this
7337              */
7338         enable : true,
7339         /**
7340          * @event beforeshow
7341          * Fires before the component is shown.  Return false to stop the show.
7342              * @param {Roo.Component} this
7343              */
7344         beforeshow : true,
7345         /**
7346          * @event show
7347          * Fires after the component is shown.
7348              * @param {Roo.Component} this
7349              */
7350         show : true,
7351         /**
7352          * @event beforehide
7353          * Fires before the component is hidden. Return false to stop the hide.
7354              * @param {Roo.Component} this
7355              */
7356         beforehide : true,
7357         /**
7358          * @event hide
7359          * Fires after the component is hidden.
7360              * @param {Roo.Component} this
7361              */
7362         hide : true,
7363         /**
7364          * @event beforerender
7365          * Fires before the component is rendered. Return false to stop the render.
7366              * @param {Roo.Component} this
7367              */
7368         beforerender : true,
7369         /**
7370          * @event render
7371          * Fires after the component is rendered.
7372              * @param {Roo.Component} this
7373              */
7374         render : true,
7375         /**
7376          * @event beforedestroy
7377          * Fires before the component is destroyed. Return false to stop the destroy.
7378              * @param {Roo.Component} this
7379              */
7380         beforedestroy : true,
7381         /**
7382          * @event destroy
7383          * Fires after the component is destroyed.
7384              * @param {Roo.Component} this
7385              */
7386         destroy : true
7387     });
7388     if(!this.id){
7389         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7390     }
7391     Roo.ComponentMgr.register(this);
7392     Roo.Component.superclass.constructor.call(this);
7393     this.initComponent();
7394     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7395         this.render(this.renderTo);
7396         delete this.renderTo;
7397     }
7398 };
7399
7400 /** @private */
7401 Roo.Component.AUTO_ID = 1000;
7402
7403 Roo.extend(Roo.Component, Roo.util.Observable, {
7404     /**
7405      * @scope Roo.Component.prototype
7406      * @type {Boolean}
7407      * true if this component is hidden. Read-only.
7408      */
7409     hidden : false,
7410     /**
7411      * @type {Boolean}
7412      * true if this component is disabled. Read-only.
7413      */
7414     disabled : false,
7415     /**
7416      * @type {Boolean}
7417      * true if this component has been rendered. Read-only.
7418      */
7419     rendered : false,
7420     
7421     /** @cfg {String} disableClass
7422      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7423      */
7424     disabledClass : "x-item-disabled",
7425         /** @cfg {Boolean} allowDomMove
7426          * Whether the component can move the Dom node when rendering (defaults to true).
7427          */
7428     allowDomMove : true,
7429     /** @cfg {String} hideMode
7430      * How this component should hidden. Supported values are
7431      * "visibility" (css visibility), "offsets" (negative offset position) and
7432      * "display" (css display) - defaults to "display".
7433      */
7434     hideMode: 'display',
7435
7436     /** @private */
7437     ctype : "Roo.Component",
7438
7439     /**
7440      * @cfg {String} actionMode 
7441      * which property holds the element that used for  hide() / show() / disable() / enable()
7442      * default is 'el' 
7443      */
7444     actionMode : "el",
7445
7446     /** @private */
7447     getActionEl : function(){
7448         return this[this.actionMode];
7449     },
7450
7451     initComponent : Roo.emptyFn,
7452     /**
7453      * If this is a lazy rendering component, render it to its container element.
7454      * @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.
7455      */
7456     render : function(container, position){
7457         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7458             if(!container && this.el){
7459                 this.el = Roo.get(this.el);
7460                 container = this.el.dom.parentNode;
7461                 this.allowDomMove = false;
7462             }
7463             this.container = Roo.get(container);
7464             this.rendered = true;
7465             if(position !== undefined){
7466                 if(typeof position == 'number'){
7467                     position = this.container.dom.childNodes[position];
7468                 }else{
7469                     position = Roo.getDom(position);
7470                 }
7471             }
7472             this.onRender(this.container, position || null);
7473             if(this.cls){
7474                 this.el.addClass(this.cls);
7475                 delete this.cls;
7476             }
7477             if(this.style){
7478                 this.el.applyStyles(this.style);
7479                 delete this.style;
7480             }
7481             this.fireEvent("render", this);
7482             this.afterRender(this.container);
7483             if(this.hidden){
7484                 this.hide();
7485             }
7486             if(this.disabled){
7487                 this.disable();
7488             }
7489         }
7490         return this;
7491     },
7492
7493     /** @private */
7494     // default function is not really useful
7495     onRender : function(ct, position){
7496         if(this.el){
7497             this.el = Roo.get(this.el);
7498             if(this.allowDomMove !== false){
7499                 ct.dom.insertBefore(this.el.dom, position);
7500             }
7501         }
7502     },
7503
7504     /** @private */
7505     getAutoCreate : function(){
7506         var cfg = typeof this.autoCreate == "object" ?
7507                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7508         if(this.id && !cfg.id){
7509             cfg.id = this.id;
7510         }
7511         return cfg;
7512     },
7513
7514     /** @private */
7515     afterRender : Roo.emptyFn,
7516
7517     /**
7518      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7519      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7520      */
7521     destroy : function(){
7522         if(this.fireEvent("beforedestroy", this) !== false){
7523             this.purgeListeners();
7524             this.beforeDestroy();
7525             if(this.rendered){
7526                 this.el.removeAllListeners();
7527                 this.el.remove();
7528                 if(this.actionMode == "container"){
7529                     this.container.remove();
7530                 }
7531             }
7532             this.onDestroy();
7533             Roo.ComponentMgr.unregister(this);
7534             this.fireEvent("destroy", this);
7535         }
7536     },
7537
7538         /** @private */
7539     beforeDestroy : function(){
7540
7541     },
7542
7543         /** @private */
7544         onDestroy : function(){
7545
7546     },
7547
7548     /**
7549      * Returns the underlying {@link Roo.Element}.
7550      * @return {Roo.Element} The element
7551      */
7552     getEl : function(){
7553         return this.el;
7554     },
7555
7556     /**
7557      * Returns the id of this component.
7558      * @return {String}
7559      */
7560     getId : function(){
7561         return this.id;
7562     },
7563
7564     /**
7565      * Try to focus this component.
7566      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7567      * @return {Roo.Component} this
7568      */
7569     focus : function(selectText){
7570         if(this.rendered){
7571             this.el.focus();
7572             if(selectText === true){
7573                 this.el.dom.select();
7574             }
7575         }
7576         return this;
7577     },
7578
7579     /** @private */
7580     blur : function(){
7581         if(this.rendered){
7582             this.el.blur();
7583         }
7584         return this;
7585     },
7586
7587     /**
7588      * Disable this component.
7589      * @return {Roo.Component} this
7590      */
7591     disable : function(){
7592         if(this.rendered){
7593             this.onDisable();
7594         }
7595         this.disabled = true;
7596         this.fireEvent("disable", this);
7597         return this;
7598     },
7599
7600         // private
7601     onDisable : function(){
7602         this.getActionEl().addClass(this.disabledClass);
7603         this.el.dom.disabled = true;
7604     },
7605
7606     /**
7607      * Enable this component.
7608      * @return {Roo.Component} this
7609      */
7610     enable : function(){
7611         if(this.rendered){
7612             this.onEnable();
7613         }
7614         this.disabled = false;
7615         this.fireEvent("enable", this);
7616         return this;
7617     },
7618
7619         // private
7620     onEnable : function(){
7621         this.getActionEl().removeClass(this.disabledClass);
7622         this.el.dom.disabled = false;
7623     },
7624
7625     /**
7626      * Convenience function for setting disabled/enabled by boolean.
7627      * @param {Boolean} disabled
7628      */
7629     setDisabled : function(disabled){
7630         this[disabled ? "disable" : "enable"]();
7631     },
7632
7633     /**
7634      * Show this component.
7635      * @return {Roo.Component} this
7636      */
7637     show: function(){
7638         if(this.fireEvent("beforeshow", this) !== false){
7639             this.hidden = false;
7640             if(this.rendered){
7641                 this.onShow();
7642             }
7643             this.fireEvent("show", this);
7644         }
7645         return this;
7646     },
7647
7648     // private
7649     onShow : function(){
7650         var ae = this.getActionEl();
7651         if(this.hideMode == 'visibility'){
7652             ae.dom.style.visibility = "visible";
7653         }else if(this.hideMode == 'offsets'){
7654             ae.removeClass('x-hidden');
7655         }else{
7656             ae.dom.style.display = "";
7657         }
7658     },
7659
7660     /**
7661      * Hide this component.
7662      * @return {Roo.Component} this
7663      */
7664     hide: function(){
7665         if(this.fireEvent("beforehide", this) !== false){
7666             this.hidden = true;
7667             if(this.rendered){
7668                 this.onHide();
7669             }
7670             this.fireEvent("hide", this);
7671         }
7672         return this;
7673     },
7674
7675     // private
7676     onHide : function(){
7677         var ae = this.getActionEl();
7678         if(this.hideMode == 'visibility'){
7679             ae.dom.style.visibility = "hidden";
7680         }else if(this.hideMode == 'offsets'){
7681             ae.addClass('x-hidden');
7682         }else{
7683             ae.dom.style.display = "none";
7684         }
7685     },
7686
7687     /**
7688      * Convenience function to hide or show this component by boolean.
7689      * @param {Boolean} visible True to show, false to hide
7690      * @return {Roo.Component} this
7691      */
7692     setVisible: function(visible){
7693         if(visible) {
7694             this.show();
7695         }else{
7696             this.hide();
7697         }
7698         return this;
7699     },
7700
7701     /**
7702      * Returns true if this component is visible.
7703      */
7704     isVisible : function(){
7705         return this.getActionEl().isVisible();
7706     },
7707
7708     cloneConfig : function(overrides){
7709         overrides = overrides || {};
7710         var id = overrides.id || Roo.id();
7711         var cfg = Roo.applyIf(overrides, this.initialConfig);
7712         cfg.id = id; // prevent dup id
7713         return new this.constructor(cfg);
7714     }
7715 });/*
7716  * Based on:
7717  * Ext JS Library 1.1.1
7718  * Copyright(c) 2006-2007, Ext JS, LLC.
7719  *
7720  * Originally Released Under LGPL - original licence link has changed is not relivant.
7721  *
7722  * Fork - LGPL
7723  * <script type="text/javascript">
7724  */
7725  (function(){ 
7726 /**
7727  * @class Roo.Layer
7728  * @extends Roo.Element
7729  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7730  * automatic maintaining of shadow/shim positions.
7731  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7732  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7733  * you can pass a string with a CSS class name. False turns off the shadow.
7734  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7735  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7736  * @cfg {String} cls CSS class to add to the element
7737  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7738  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7739  * @constructor
7740  * @param {Object} config An object with config options.
7741  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7742  */
7743
7744 Roo.Layer = function(config, existingEl){
7745     config = config || {};
7746     var dh = Roo.DomHelper;
7747     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7748     if(existingEl){
7749         this.dom = Roo.getDom(existingEl);
7750     }
7751     if(!this.dom){
7752         var o = config.dh || {tag: "div", cls: "x-layer"};
7753         this.dom = dh.append(pel, o);
7754     }
7755     if(config.cls){
7756         this.addClass(config.cls);
7757     }
7758     this.constrain = config.constrain !== false;
7759     this.visibilityMode = Roo.Element.VISIBILITY;
7760     if(config.id){
7761         this.id = this.dom.id = config.id;
7762     }else{
7763         this.id = Roo.id(this.dom);
7764     }
7765     this.zindex = config.zindex || this.getZIndex();
7766     this.position("absolute", this.zindex);
7767     if(config.shadow){
7768         this.shadowOffset = config.shadowOffset || 4;
7769         this.shadow = new Roo.Shadow({
7770             offset : this.shadowOffset,
7771             mode : config.shadow
7772         });
7773     }else{
7774         this.shadowOffset = 0;
7775     }
7776     this.useShim = config.shim !== false && Roo.useShims;
7777     this.useDisplay = config.useDisplay;
7778     this.hide();
7779 };
7780
7781 var supr = Roo.Element.prototype;
7782
7783 // shims are shared among layer to keep from having 100 iframes
7784 var shims = [];
7785
7786 Roo.extend(Roo.Layer, Roo.Element, {
7787
7788     getZIndex : function(){
7789         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7790     },
7791
7792     getShim : function(){
7793         if(!this.useShim){
7794             return null;
7795         }
7796         if(this.shim){
7797             return this.shim;
7798         }
7799         var shim = shims.shift();
7800         if(!shim){
7801             shim = this.createShim();
7802             shim.enableDisplayMode('block');
7803             shim.dom.style.display = 'none';
7804             shim.dom.style.visibility = 'visible';
7805         }
7806         var pn = this.dom.parentNode;
7807         if(shim.dom.parentNode != pn){
7808             pn.insertBefore(shim.dom, this.dom);
7809         }
7810         shim.setStyle('z-index', this.getZIndex()-2);
7811         this.shim = shim;
7812         return shim;
7813     },
7814
7815     hideShim : function(){
7816         if(this.shim){
7817             this.shim.setDisplayed(false);
7818             shims.push(this.shim);
7819             delete this.shim;
7820         }
7821     },
7822
7823     disableShadow : function(){
7824         if(this.shadow){
7825             this.shadowDisabled = true;
7826             this.shadow.hide();
7827             this.lastShadowOffset = this.shadowOffset;
7828             this.shadowOffset = 0;
7829         }
7830     },
7831
7832     enableShadow : function(show){
7833         if(this.shadow){
7834             this.shadowDisabled = false;
7835             this.shadowOffset = this.lastShadowOffset;
7836             delete this.lastShadowOffset;
7837             if(show){
7838                 this.sync(true);
7839             }
7840         }
7841     },
7842
7843     // private
7844     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7845     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7846     sync : function(doShow){
7847         var sw = this.shadow;
7848         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7849             var sh = this.getShim();
7850
7851             var w = this.getWidth(),
7852                 h = this.getHeight();
7853
7854             var l = this.getLeft(true),
7855                 t = this.getTop(true);
7856
7857             if(sw && !this.shadowDisabled){
7858                 if(doShow && !sw.isVisible()){
7859                     sw.show(this);
7860                 }else{
7861                     sw.realign(l, t, w, h);
7862                 }
7863                 if(sh){
7864                     if(doShow){
7865                        sh.show();
7866                     }
7867                     // fit the shim behind the shadow, so it is shimmed too
7868                     var a = sw.adjusts, s = sh.dom.style;
7869                     s.left = (Math.min(l, l+a.l))+"px";
7870                     s.top = (Math.min(t, t+a.t))+"px";
7871                     s.width = (w+a.w)+"px";
7872                     s.height = (h+a.h)+"px";
7873                 }
7874             }else if(sh){
7875                 if(doShow){
7876                    sh.show();
7877                 }
7878                 sh.setSize(w, h);
7879                 sh.setLeftTop(l, t);
7880             }
7881             
7882         }
7883     },
7884
7885     // private
7886     destroy : function(){
7887         this.hideShim();
7888         if(this.shadow){
7889             this.shadow.hide();
7890         }
7891         this.removeAllListeners();
7892         var pn = this.dom.parentNode;
7893         if(pn){
7894             pn.removeChild(this.dom);
7895         }
7896         Roo.Element.uncache(this.id);
7897     },
7898
7899     remove : function(){
7900         this.destroy();
7901     },
7902
7903     // private
7904     beginUpdate : function(){
7905         this.updating = true;
7906     },
7907
7908     // private
7909     endUpdate : function(){
7910         this.updating = false;
7911         this.sync(true);
7912     },
7913
7914     // private
7915     hideUnders : function(negOffset){
7916         if(this.shadow){
7917             this.shadow.hide();
7918         }
7919         this.hideShim();
7920     },
7921
7922     // private
7923     constrainXY : function(){
7924         if(this.constrain){
7925             var vw = Roo.lib.Dom.getViewWidth(),
7926                 vh = Roo.lib.Dom.getViewHeight();
7927             var s = Roo.get(document).getScroll();
7928
7929             var xy = this.getXY();
7930             var x = xy[0], y = xy[1];   
7931             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7932             // only move it if it needs it
7933             var moved = false;
7934             // first validate right/bottom
7935             if((x + w) > vw+s.left){
7936                 x = vw - w - this.shadowOffset;
7937                 moved = true;
7938             }
7939             if((y + h) > vh+s.top){
7940                 y = vh - h - this.shadowOffset;
7941                 moved = true;
7942             }
7943             // then make sure top/left isn't negative
7944             if(x < s.left){
7945                 x = s.left;
7946                 moved = true;
7947             }
7948             if(y < s.top){
7949                 y = s.top;
7950                 moved = true;
7951             }
7952             if(moved){
7953                 if(this.avoidY){
7954                     var ay = this.avoidY;
7955                     if(y <= ay && (y+h) >= ay){
7956                         y = ay-h-5;   
7957                     }
7958                 }
7959                 xy = [x, y];
7960                 this.storeXY(xy);
7961                 supr.setXY.call(this, xy);
7962                 this.sync();
7963             }
7964         }
7965     },
7966
7967     isVisible : function(){
7968         return this.visible;    
7969     },
7970
7971     // private
7972     showAction : function(){
7973         this.visible = true; // track visibility to prevent getStyle calls
7974         if(this.useDisplay === true){
7975             this.setDisplayed("");
7976         }else if(this.lastXY){
7977             supr.setXY.call(this, this.lastXY);
7978         }else if(this.lastLT){
7979             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7980         }
7981     },
7982
7983     // private
7984     hideAction : function(){
7985         this.visible = false;
7986         if(this.useDisplay === true){
7987             this.setDisplayed(false);
7988         }else{
7989             this.setLeftTop(-10000,-10000);
7990         }
7991     },
7992
7993     // overridden Element method
7994     setVisible : function(v, a, d, c, e){
7995         if(v){
7996             this.showAction();
7997         }
7998         if(a && v){
7999             var cb = function(){
8000                 this.sync(true);
8001                 if(c){
8002                     c();
8003                 }
8004             }.createDelegate(this);
8005             supr.setVisible.call(this, true, true, d, cb, e);
8006         }else{
8007             if(!v){
8008                 this.hideUnders(true);
8009             }
8010             var cb = c;
8011             if(a){
8012                 cb = function(){
8013                     this.hideAction();
8014                     if(c){
8015                         c();
8016                     }
8017                 }.createDelegate(this);
8018             }
8019             supr.setVisible.call(this, v, a, d, cb, e);
8020             if(v){
8021                 this.sync(true);
8022             }else if(!a){
8023                 this.hideAction();
8024             }
8025         }
8026     },
8027
8028     storeXY : function(xy){
8029         delete this.lastLT;
8030         this.lastXY = xy;
8031     },
8032
8033     storeLeftTop : function(left, top){
8034         delete this.lastXY;
8035         this.lastLT = [left, top];
8036     },
8037
8038     // private
8039     beforeFx : function(){
8040         this.beforeAction();
8041         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8042     },
8043
8044     // private
8045     afterFx : function(){
8046         Roo.Layer.superclass.afterFx.apply(this, arguments);
8047         this.sync(this.isVisible());
8048     },
8049
8050     // private
8051     beforeAction : function(){
8052         if(!this.updating && this.shadow){
8053             this.shadow.hide();
8054         }
8055     },
8056
8057     // overridden Element method
8058     setLeft : function(left){
8059         this.storeLeftTop(left, this.getTop(true));
8060         supr.setLeft.apply(this, arguments);
8061         this.sync();
8062     },
8063
8064     setTop : function(top){
8065         this.storeLeftTop(this.getLeft(true), top);
8066         supr.setTop.apply(this, arguments);
8067         this.sync();
8068     },
8069
8070     setLeftTop : function(left, top){
8071         this.storeLeftTop(left, top);
8072         supr.setLeftTop.apply(this, arguments);
8073         this.sync();
8074     },
8075
8076     setXY : function(xy, a, d, c, e){
8077         this.fixDisplay();
8078         this.beforeAction();
8079         this.storeXY(xy);
8080         var cb = this.createCB(c);
8081         supr.setXY.call(this, xy, a, d, cb, e);
8082         if(!a){
8083             cb();
8084         }
8085     },
8086
8087     // private
8088     createCB : function(c){
8089         var el = this;
8090         return function(){
8091             el.constrainXY();
8092             el.sync(true);
8093             if(c){
8094                 c();
8095             }
8096         };
8097     },
8098
8099     // overridden Element method
8100     setX : function(x, a, d, c, e){
8101         this.setXY([x, this.getY()], a, d, c, e);
8102     },
8103
8104     // overridden Element method
8105     setY : function(y, a, d, c, e){
8106         this.setXY([this.getX(), y], a, d, c, e);
8107     },
8108
8109     // overridden Element method
8110     setSize : function(w, h, a, d, c, e){
8111         this.beforeAction();
8112         var cb = this.createCB(c);
8113         supr.setSize.call(this, w, h, a, d, cb, e);
8114         if(!a){
8115             cb();
8116         }
8117     },
8118
8119     // overridden Element method
8120     setWidth : function(w, a, d, c, e){
8121         this.beforeAction();
8122         var cb = this.createCB(c);
8123         supr.setWidth.call(this, w, a, d, cb, e);
8124         if(!a){
8125             cb();
8126         }
8127     },
8128
8129     // overridden Element method
8130     setHeight : function(h, a, d, c, e){
8131         this.beforeAction();
8132         var cb = this.createCB(c);
8133         supr.setHeight.call(this, h, a, d, cb, e);
8134         if(!a){
8135             cb();
8136         }
8137     },
8138
8139     // overridden Element method
8140     setBounds : function(x, y, w, h, a, d, c, e){
8141         this.beforeAction();
8142         var cb = this.createCB(c);
8143         if(!a){
8144             this.storeXY([x, y]);
8145             supr.setXY.call(this, [x, y]);
8146             supr.setSize.call(this, w, h, a, d, cb, e);
8147             cb();
8148         }else{
8149             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8150         }
8151         return this;
8152     },
8153     
8154     /**
8155      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8156      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8157      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8158      * @param {Number} zindex The new z-index to set
8159      * @return {this} The Layer
8160      */
8161     setZIndex : function(zindex){
8162         this.zindex = zindex;
8163         this.setStyle("z-index", zindex + 2);
8164         if(this.shadow){
8165             this.shadow.setZIndex(zindex + 1);
8166         }
8167         if(this.shim){
8168             this.shim.setStyle("z-index", zindex);
8169         }
8170     }
8171 });
8172 })();/*
8173  * Based on:
8174  * Ext JS Library 1.1.1
8175  * Copyright(c) 2006-2007, Ext JS, LLC.
8176  *
8177  * Originally Released Under LGPL - original licence link has changed is not relivant.
8178  *
8179  * Fork - LGPL
8180  * <script type="text/javascript">
8181  */
8182
8183
8184 /**
8185  * @class Roo.Shadow
8186  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8187  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8188  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8189  * @constructor
8190  * Create a new Shadow
8191  * @param {Object} config The config object
8192  */
8193 Roo.Shadow = function(config){
8194     Roo.apply(this, config);
8195     if(typeof this.mode != "string"){
8196         this.mode = this.defaultMode;
8197     }
8198     var o = this.offset, a = {h: 0};
8199     var rad = Math.floor(this.offset/2);
8200     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8201         case "drop":
8202             a.w = 0;
8203             a.l = a.t = o;
8204             a.t -= 1;
8205             if(Roo.isIE){
8206                 a.l -= this.offset + rad;
8207                 a.t -= this.offset + rad;
8208                 a.w -= rad;
8209                 a.h -= rad;
8210                 a.t += 1;
8211             }
8212         break;
8213         case "sides":
8214             a.w = (o*2);
8215             a.l = -o;
8216             a.t = o-1;
8217             if(Roo.isIE){
8218                 a.l -= (this.offset - rad);
8219                 a.t -= this.offset + rad;
8220                 a.l += 1;
8221                 a.w -= (this.offset - rad)*2;
8222                 a.w -= rad + 1;
8223                 a.h -= 1;
8224             }
8225         break;
8226         case "frame":
8227             a.w = a.h = (o*2);
8228             a.l = a.t = -o;
8229             a.t += 1;
8230             a.h -= 2;
8231             if(Roo.isIE){
8232                 a.l -= (this.offset - rad);
8233                 a.t -= (this.offset - rad);
8234                 a.l += 1;
8235                 a.w -= (this.offset + rad + 1);
8236                 a.h -= (this.offset + rad);
8237                 a.h += 1;
8238             }
8239         break;
8240     };
8241
8242     this.adjusts = a;
8243 };
8244
8245 Roo.Shadow.prototype = {
8246     /**
8247      * @cfg {String} mode
8248      * The shadow display mode.  Supports the following options:<br />
8249      * sides: Shadow displays on both sides and bottom only<br />
8250      * frame: Shadow displays equally on all four sides<br />
8251      * drop: Traditional bottom-right drop shadow (default)
8252      */
8253     /**
8254      * @cfg {String} offset
8255      * The number of pixels to offset the shadow from the element (defaults to 4)
8256      */
8257     offset: 4,
8258
8259     // private
8260     defaultMode: "drop",
8261
8262     /**
8263      * Displays the shadow under the target element
8264      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8265      */
8266     show : function(target){
8267         target = Roo.get(target);
8268         if(!this.el){
8269             this.el = Roo.Shadow.Pool.pull();
8270             if(this.el.dom.nextSibling != target.dom){
8271                 this.el.insertBefore(target);
8272             }
8273         }
8274         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8275         if(Roo.isIE){
8276             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8277         }
8278         this.realign(
8279             target.getLeft(true),
8280             target.getTop(true),
8281             target.getWidth(),
8282             target.getHeight()
8283         );
8284         this.el.dom.style.display = "block";
8285     },
8286
8287     /**
8288      * Returns true if the shadow is visible, else false
8289      */
8290     isVisible : function(){
8291         return this.el ? true : false;  
8292     },
8293
8294     /**
8295      * Direct alignment when values are already available. Show must be called at least once before
8296      * calling this method to ensure it is initialized.
8297      * @param {Number} left The target element left position
8298      * @param {Number} top The target element top position
8299      * @param {Number} width The target element width
8300      * @param {Number} height The target element height
8301      */
8302     realign : function(l, t, w, h){
8303         if(!this.el){
8304             return;
8305         }
8306         var a = this.adjusts, d = this.el.dom, s = d.style;
8307         var iea = 0;
8308         s.left = (l+a.l)+"px";
8309         s.top = (t+a.t)+"px";
8310         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8311  
8312         if(s.width != sws || s.height != shs){
8313             s.width = sws;
8314             s.height = shs;
8315             if(!Roo.isIE){
8316                 var cn = d.childNodes;
8317                 var sww = Math.max(0, (sw-12))+"px";
8318                 cn[0].childNodes[1].style.width = sww;
8319                 cn[1].childNodes[1].style.width = sww;
8320                 cn[2].childNodes[1].style.width = sww;
8321                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8322             }
8323         }
8324     },
8325
8326     /**
8327      * Hides this shadow
8328      */
8329     hide : function(){
8330         if(this.el){
8331             this.el.dom.style.display = "none";
8332             Roo.Shadow.Pool.push(this.el);
8333             delete this.el;
8334         }
8335     },
8336
8337     /**
8338      * Adjust the z-index of this shadow
8339      * @param {Number} zindex The new z-index
8340      */
8341     setZIndex : function(z){
8342         this.zIndex = z;
8343         if(this.el){
8344             this.el.setStyle("z-index", z);
8345         }
8346     }
8347 };
8348
8349 // Private utility class that manages the internal Shadow cache
8350 Roo.Shadow.Pool = function(){
8351     var p = [];
8352     var markup = Roo.isIE ?
8353                  '<div class="x-ie-shadow"></div>' :
8354                  '<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>';
8355     return {
8356         pull : function(){
8357             var sh = p.shift();
8358             if(!sh){
8359                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8360                 sh.autoBoxAdjust = false;
8361             }
8362             return sh;
8363         },
8364
8365         push : function(sh){
8366             p.push(sh);
8367         }
8368     };
8369 }();/*
8370  * Based on:
8371  * Ext JS Library 1.1.1
8372  * Copyright(c) 2006-2007, Ext JS, LLC.
8373  *
8374  * Originally Released Under LGPL - original licence link has changed is not relivant.
8375  *
8376  * Fork - LGPL
8377  * <script type="text/javascript">
8378  */
8379
8380 /**
8381  * @class Roo.BoxComponent
8382  * @extends Roo.Component
8383  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8384  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8385  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8386  * layout containers.
8387  * @constructor
8388  * @param {Roo.Element/String/Object} config The configuration options.
8389  */
8390 Roo.BoxComponent = function(config){
8391     Roo.Component.call(this, config);
8392     this.addEvents({
8393         /**
8394          * @event resize
8395          * Fires after the component is resized.
8396              * @param {Roo.Component} this
8397              * @param {Number} adjWidth The box-adjusted width that was set
8398              * @param {Number} adjHeight The box-adjusted height that was set
8399              * @param {Number} rawWidth The width that was originally specified
8400              * @param {Number} rawHeight The height that was originally specified
8401              */
8402         resize : true,
8403         /**
8404          * @event move
8405          * Fires after the component is moved.
8406              * @param {Roo.Component} this
8407              * @param {Number} x The new x position
8408              * @param {Number} y The new y position
8409              */
8410         move : true
8411     });
8412 };
8413
8414 Roo.extend(Roo.BoxComponent, Roo.Component, {
8415     // private, set in afterRender to signify that the component has been rendered
8416     boxReady : false,
8417     // private, used to defer height settings to subclasses
8418     deferHeight: false,
8419     /** @cfg {Number} width
8420      * width (optional) size of component
8421      */
8422      /** @cfg {Number} height
8423      * height (optional) size of component
8424      */
8425      
8426     /**
8427      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8428      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8429      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8430      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8431      * @return {Roo.BoxComponent} this
8432      */
8433     setSize : function(w, h){
8434         // support for standard size objects
8435         if(typeof w == 'object'){
8436             h = w.height;
8437             w = w.width;
8438         }
8439         // not rendered
8440         if(!this.boxReady){
8441             this.width = w;
8442             this.height = h;
8443             return this;
8444         }
8445
8446         // prevent recalcs when not needed
8447         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8448             return this;
8449         }
8450         this.lastSize = {width: w, height: h};
8451
8452         var adj = this.adjustSize(w, h);
8453         var aw = adj.width, ah = adj.height;
8454         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8455             var rz = this.getResizeEl();
8456             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8457                 rz.setSize(aw, ah);
8458             }else if(!this.deferHeight && ah !== undefined){
8459                 rz.setHeight(ah);
8460             }else if(aw !== undefined){
8461                 rz.setWidth(aw);
8462             }
8463             this.onResize(aw, ah, w, h);
8464             this.fireEvent('resize', this, aw, ah, w, h);
8465         }
8466         return this;
8467     },
8468
8469     /**
8470      * Gets the current size of the component's underlying element.
8471      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8472      */
8473     getSize : function(){
8474         return this.el.getSize();
8475     },
8476
8477     /**
8478      * Gets the current XY position of the component's underlying element.
8479      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8480      * @return {Array} The XY position of the element (e.g., [100, 200])
8481      */
8482     getPosition : function(local){
8483         if(local === true){
8484             return [this.el.getLeft(true), this.el.getTop(true)];
8485         }
8486         return this.xy || this.el.getXY();
8487     },
8488
8489     /**
8490      * Gets the current box measurements of the component's underlying element.
8491      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8492      * @returns {Object} box An object in the format {x, y, width, height}
8493      */
8494     getBox : function(local){
8495         var s = this.el.getSize();
8496         if(local){
8497             s.x = this.el.getLeft(true);
8498             s.y = this.el.getTop(true);
8499         }else{
8500             var xy = this.xy || this.el.getXY();
8501             s.x = xy[0];
8502             s.y = xy[1];
8503         }
8504         return s;
8505     },
8506
8507     /**
8508      * Sets the current box measurements of the component's underlying element.
8509      * @param {Object} box An object in the format {x, y, width, height}
8510      * @returns {Roo.BoxComponent} this
8511      */
8512     updateBox : function(box){
8513         this.setSize(box.width, box.height);
8514         this.setPagePosition(box.x, box.y);
8515         return this;
8516     },
8517
8518     // protected
8519     getResizeEl : function(){
8520         return this.resizeEl || this.el;
8521     },
8522
8523     // protected
8524     getPositionEl : function(){
8525         return this.positionEl || this.el;
8526     },
8527
8528     /**
8529      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8530      * This method fires the move event.
8531      * @param {Number} left The new left
8532      * @param {Number} top The new top
8533      * @returns {Roo.BoxComponent} this
8534      */
8535     setPosition : function(x, y){
8536         this.x = x;
8537         this.y = y;
8538         if(!this.boxReady){
8539             return this;
8540         }
8541         var adj = this.adjustPosition(x, y);
8542         var ax = adj.x, ay = adj.y;
8543
8544         var el = this.getPositionEl();
8545         if(ax !== undefined || ay !== undefined){
8546             if(ax !== undefined && ay !== undefined){
8547                 el.setLeftTop(ax, ay);
8548             }else if(ax !== undefined){
8549                 el.setLeft(ax);
8550             }else if(ay !== undefined){
8551                 el.setTop(ay);
8552             }
8553             this.onPosition(ax, ay);
8554             this.fireEvent('move', this, ax, ay);
8555         }
8556         return this;
8557     },
8558
8559     /**
8560      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8561      * This method fires the move event.
8562      * @param {Number} x The new x position
8563      * @param {Number} y The new y position
8564      * @returns {Roo.BoxComponent} this
8565      */
8566     setPagePosition : function(x, y){
8567         this.pageX = x;
8568         this.pageY = y;
8569         if(!this.boxReady){
8570             return;
8571         }
8572         if(x === undefined || y === undefined){ // cannot translate undefined points
8573             return;
8574         }
8575         var p = this.el.translatePoints(x, y);
8576         this.setPosition(p.left, p.top);
8577         return this;
8578     },
8579
8580     // private
8581     onRender : function(ct, position){
8582         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8583         if(this.resizeEl){
8584             this.resizeEl = Roo.get(this.resizeEl);
8585         }
8586         if(this.positionEl){
8587             this.positionEl = Roo.get(this.positionEl);
8588         }
8589     },
8590
8591     // private
8592     afterRender : function(){
8593         Roo.BoxComponent.superclass.afterRender.call(this);
8594         this.boxReady = true;
8595         this.setSize(this.width, this.height);
8596         if(this.x || this.y){
8597             this.setPosition(this.x, this.y);
8598         }
8599         if(this.pageX || this.pageY){
8600             this.setPagePosition(this.pageX, this.pageY);
8601         }
8602     },
8603
8604     /**
8605      * Force the component's size to recalculate based on the underlying element's current height and width.
8606      * @returns {Roo.BoxComponent} this
8607      */
8608     syncSize : function(){
8609         delete this.lastSize;
8610         this.setSize(this.el.getWidth(), this.el.getHeight());
8611         return this;
8612     },
8613
8614     /**
8615      * Called after the component is resized, this method is empty by default but can be implemented by any
8616      * subclass that needs to perform custom logic after a resize occurs.
8617      * @param {Number} adjWidth The box-adjusted width that was set
8618      * @param {Number} adjHeight The box-adjusted height that was set
8619      * @param {Number} rawWidth The width that was originally specified
8620      * @param {Number} rawHeight The height that was originally specified
8621      */
8622     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8623
8624     },
8625
8626     /**
8627      * Called after the component is moved, this method is empty by default but can be implemented by any
8628      * subclass that needs to perform custom logic after a move occurs.
8629      * @param {Number} x The new x position
8630      * @param {Number} y The new y position
8631      */
8632     onPosition : function(x, y){
8633
8634     },
8635
8636     // private
8637     adjustSize : function(w, h){
8638         if(this.autoWidth){
8639             w = 'auto';
8640         }
8641         if(this.autoHeight){
8642             h = 'auto';
8643         }
8644         return {width : w, height: h};
8645     },
8646
8647     // private
8648     adjustPosition : function(x, y){
8649         return {x : x, y: y};
8650     }
8651 });/*
8652  * Based on:
8653  * Ext JS Library 1.1.1
8654  * Copyright(c) 2006-2007, Ext JS, LLC.
8655  *
8656  * Originally Released Under LGPL - original licence link has changed is not relivant.
8657  *
8658  * Fork - LGPL
8659  * <script type="text/javascript">
8660  */
8661
8662
8663 /**
8664  * @class Roo.SplitBar
8665  * @extends Roo.util.Observable
8666  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8667  * <br><br>
8668  * Usage:
8669  * <pre><code>
8670 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8671                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8672 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8673 split.minSize = 100;
8674 split.maxSize = 600;
8675 split.animate = true;
8676 split.on('moved', splitterMoved);
8677 </code></pre>
8678  * @constructor
8679  * Create a new SplitBar
8680  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8681  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8682  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8683  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8684                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8685                         position of the SplitBar).
8686  */
8687 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8688     
8689     /** @private */
8690     this.el = Roo.get(dragElement, true);
8691     this.el.dom.unselectable = "on";
8692     /** @private */
8693     this.resizingEl = Roo.get(resizingElement, true);
8694
8695     /**
8696      * @private
8697      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8698      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8699      * @type Number
8700      */
8701     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8702     
8703     /**
8704      * The minimum size of the resizing element. (Defaults to 0)
8705      * @type Number
8706      */
8707     this.minSize = 0;
8708     
8709     /**
8710      * The maximum size of the resizing element. (Defaults to 2000)
8711      * @type Number
8712      */
8713     this.maxSize = 2000;
8714     
8715     /**
8716      * Whether to animate the transition to the new size
8717      * @type Boolean
8718      */
8719     this.animate = false;
8720     
8721     /**
8722      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8723      * @type Boolean
8724      */
8725     this.useShim = false;
8726     
8727     /** @private */
8728     this.shim = null;
8729     
8730     if(!existingProxy){
8731         /** @private */
8732         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8733     }else{
8734         this.proxy = Roo.get(existingProxy).dom;
8735     }
8736     /** @private */
8737     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8738     
8739     /** @private */
8740     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8741     
8742     /** @private */
8743     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8744     
8745     /** @private */
8746     this.dragSpecs = {};
8747     
8748     /**
8749      * @private The adapter to use to positon and resize elements
8750      */
8751     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8752     this.adapter.init(this);
8753     
8754     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8755         /** @private */
8756         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8757         this.el.addClass("x-splitbar-h");
8758     }else{
8759         /** @private */
8760         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8761         this.el.addClass("x-splitbar-v");
8762     }
8763     
8764     this.addEvents({
8765         /**
8766          * @event resize
8767          * Fires when the splitter is moved (alias for {@link #event-moved})
8768          * @param {Roo.SplitBar} this
8769          * @param {Number} newSize the new width or height
8770          */
8771         "resize" : true,
8772         /**
8773          * @event moved
8774          * Fires when the splitter is moved
8775          * @param {Roo.SplitBar} this
8776          * @param {Number} newSize the new width or height
8777          */
8778         "moved" : true,
8779         /**
8780          * @event beforeresize
8781          * Fires before the splitter is dragged
8782          * @param {Roo.SplitBar} this
8783          */
8784         "beforeresize" : true,
8785
8786         "beforeapply" : true
8787     });
8788
8789     Roo.util.Observable.call(this);
8790 };
8791
8792 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8793     onStartProxyDrag : function(x, y){
8794         this.fireEvent("beforeresize", this);
8795         if(!this.overlay){
8796             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8797             o.unselectable();
8798             o.enableDisplayMode("block");
8799             // all splitbars share the same overlay
8800             Roo.SplitBar.prototype.overlay = o;
8801         }
8802         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8803         this.overlay.show();
8804         Roo.get(this.proxy).setDisplayed("block");
8805         var size = this.adapter.getElementSize(this);
8806         this.activeMinSize = this.getMinimumSize();;
8807         this.activeMaxSize = this.getMaximumSize();;
8808         var c1 = size - this.activeMinSize;
8809         var c2 = Math.max(this.activeMaxSize - size, 0);
8810         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8811             this.dd.resetConstraints();
8812             this.dd.setXConstraint(
8813                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8814                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8815             );
8816             this.dd.setYConstraint(0, 0);
8817         }else{
8818             this.dd.resetConstraints();
8819             this.dd.setXConstraint(0, 0);
8820             this.dd.setYConstraint(
8821                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8822                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8823             );
8824          }
8825         this.dragSpecs.startSize = size;
8826         this.dragSpecs.startPoint = [x, y];
8827         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8828     },
8829     
8830     /** 
8831      * @private Called after the drag operation by the DDProxy
8832      */
8833     onEndProxyDrag : function(e){
8834         Roo.get(this.proxy).setDisplayed(false);
8835         var endPoint = Roo.lib.Event.getXY(e);
8836         if(this.overlay){
8837             this.overlay.hide();
8838         }
8839         var newSize;
8840         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8841             newSize = this.dragSpecs.startSize + 
8842                 (this.placement == Roo.SplitBar.LEFT ?
8843                     endPoint[0] - this.dragSpecs.startPoint[0] :
8844                     this.dragSpecs.startPoint[0] - endPoint[0]
8845                 );
8846         }else{
8847             newSize = this.dragSpecs.startSize + 
8848                 (this.placement == Roo.SplitBar.TOP ?
8849                     endPoint[1] - this.dragSpecs.startPoint[1] :
8850                     this.dragSpecs.startPoint[1] - endPoint[1]
8851                 );
8852         }
8853         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8854         if(newSize != this.dragSpecs.startSize){
8855             if(this.fireEvent('beforeapply', this, newSize) !== false){
8856                 this.adapter.setElementSize(this, newSize);
8857                 this.fireEvent("moved", this, newSize);
8858                 this.fireEvent("resize", this, newSize);
8859             }
8860         }
8861     },
8862     
8863     /**
8864      * Get the adapter this SplitBar uses
8865      * @return The adapter object
8866      */
8867     getAdapter : function(){
8868         return this.adapter;
8869     },
8870     
8871     /**
8872      * Set the adapter this SplitBar uses
8873      * @param {Object} adapter A SplitBar adapter object
8874      */
8875     setAdapter : function(adapter){
8876         this.adapter = adapter;
8877         this.adapter.init(this);
8878     },
8879     
8880     /**
8881      * Gets the minimum size for the resizing element
8882      * @return {Number} The minimum size
8883      */
8884     getMinimumSize : function(){
8885         return this.minSize;
8886     },
8887     
8888     /**
8889      * Sets the minimum size for the resizing element
8890      * @param {Number} minSize The minimum size
8891      */
8892     setMinimumSize : function(minSize){
8893         this.minSize = minSize;
8894     },
8895     
8896     /**
8897      * Gets the maximum size for the resizing element
8898      * @return {Number} The maximum size
8899      */
8900     getMaximumSize : function(){
8901         return this.maxSize;
8902     },
8903     
8904     /**
8905      * Sets the maximum size for the resizing element
8906      * @param {Number} maxSize The maximum size
8907      */
8908     setMaximumSize : function(maxSize){
8909         this.maxSize = maxSize;
8910     },
8911     
8912     /**
8913      * Sets the initialize size for the resizing element
8914      * @param {Number} size The initial size
8915      */
8916     setCurrentSize : function(size){
8917         var oldAnimate = this.animate;
8918         this.animate = false;
8919         this.adapter.setElementSize(this, size);
8920         this.animate = oldAnimate;
8921     },
8922     
8923     /**
8924      * Destroy this splitbar. 
8925      * @param {Boolean} removeEl True to remove the element
8926      */
8927     destroy : function(removeEl){
8928         if(this.shim){
8929             this.shim.remove();
8930         }
8931         this.dd.unreg();
8932         this.proxy.parentNode.removeChild(this.proxy);
8933         if(removeEl){
8934             this.el.remove();
8935         }
8936     }
8937 });
8938
8939 /**
8940  * @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.
8941  */
8942 Roo.SplitBar.createProxy = function(dir){
8943     var proxy = new Roo.Element(document.createElement("div"));
8944     proxy.unselectable();
8945     var cls = 'x-splitbar-proxy';
8946     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8947     document.body.appendChild(proxy.dom);
8948     return proxy.dom;
8949 };
8950
8951 /** 
8952  * @class Roo.SplitBar.BasicLayoutAdapter
8953  * Default Adapter. It assumes the splitter and resizing element are not positioned
8954  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8955  */
8956 Roo.SplitBar.BasicLayoutAdapter = function(){
8957 };
8958
8959 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8960     // do nothing for now
8961     init : function(s){
8962     
8963     },
8964     /**
8965      * Called before drag operations to get the current size of the resizing element. 
8966      * @param {Roo.SplitBar} s The SplitBar using this adapter
8967      */
8968      getElementSize : function(s){
8969         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8970             return s.resizingEl.getWidth();
8971         }else{
8972             return s.resizingEl.getHeight();
8973         }
8974     },
8975     
8976     /**
8977      * Called after drag operations to set the size of the resizing element.
8978      * @param {Roo.SplitBar} s The SplitBar using this adapter
8979      * @param {Number} newSize The new size to set
8980      * @param {Function} onComplete A function to be invoked when resizing is complete
8981      */
8982     setElementSize : function(s, newSize, onComplete){
8983         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8984             if(!s.animate){
8985                 s.resizingEl.setWidth(newSize);
8986                 if(onComplete){
8987                     onComplete(s, newSize);
8988                 }
8989             }else{
8990                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8991             }
8992         }else{
8993             
8994             if(!s.animate){
8995                 s.resizingEl.setHeight(newSize);
8996                 if(onComplete){
8997                     onComplete(s, newSize);
8998                 }
8999             }else{
9000                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
9001             }
9002         }
9003     }
9004 };
9005
9006 /** 
9007  *@class Roo.SplitBar.AbsoluteLayoutAdapter
9008  * @extends Roo.SplitBar.BasicLayoutAdapter
9009  * Adapter that  moves the splitter element to align with the resized sizing element. 
9010  * Used with an absolute positioned SplitBar.
9011  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9012  * document.body, make sure you assign an id to the body element.
9013  */
9014 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9015     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9016     this.container = Roo.get(container);
9017 };
9018
9019 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9020     init : function(s){
9021         this.basic.init(s);
9022     },
9023     
9024     getElementSize : function(s){
9025         return this.basic.getElementSize(s);
9026     },
9027     
9028     setElementSize : function(s, newSize, onComplete){
9029         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9030     },
9031     
9032     moveSplitter : function(s){
9033         var yes = Roo.SplitBar;
9034         switch(s.placement){
9035             case yes.LEFT:
9036                 s.el.setX(s.resizingEl.getRight());
9037                 break;
9038             case yes.RIGHT:
9039                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9040                 break;
9041             case yes.TOP:
9042                 s.el.setY(s.resizingEl.getBottom());
9043                 break;
9044             case yes.BOTTOM:
9045                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9046                 break;
9047         }
9048     }
9049 };
9050
9051 /**
9052  * Orientation constant - Create a vertical SplitBar
9053  * @static
9054  * @type Number
9055  */
9056 Roo.SplitBar.VERTICAL = 1;
9057
9058 /**
9059  * Orientation constant - Create a horizontal SplitBar
9060  * @static
9061  * @type Number
9062  */
9063 Roo.SplitBar.HORIZONTAL = 2;
9064
9065 /**
9066  * Placement constant - The resizing element is to the left of the splitter element
9067  * @static
9068  * @type Number
9069  */
9070 Roo.SplitBar.LEFT = 1;
9071
9072 /**
9073  * Placement constant - The resizing element is to the right of the splitter element
9074  * @static
9075  * @type Number
9076  */
9077 Roo.SplitBar.RIGHT = 2;
9078
9079 /**
9080  * Placement constant - The resizing element is positioned above the splitter element
9081  * @static
9082  * @type Number
9083  */
9084 Roo.SplitBar.TOP = 3;
9085
9086 /**
9087  * Placement constant - The resizing element is positioned under splitter element
9088  * @static
9089  * @type Number
9090  */
9091 Roo.SplitBar.BOTTOM = 4;
9092 /*
9093  * Based on:
9094  * Ext JS Library 1.1.1
9095  * Copyright(c) 2006-2007, Ext JS, LLC.
9096  *
9097  * Originally Released Under LGPL - original licence link has changed is not relivant.
9098  *
9099  * Fork - LGPL
9100  * <script type="text/javascript">
9101  */
9102
9103 /**
9104  * @class Roo.View
9105  * @extends Roo.util.Observable
9106  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9107  * This class also supports single and multi selection modes. <br>
9108  * Create a data model bound view:
9109  <pre><code>
9110  var store = new Roo.data.Store(...);
9111
9112  var view = new Roo.View({
9113     el : "my-element",
9114     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9115  
9116     singleSelect: true,
9117     selectedClass: "ydataview-selected",
9118     store: store
9119  });
9120
9121  // listen for node click?
9122  view.on("click", function(vw, index, node, e){
9123  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9124  });
9125
9126  // load XML data
9127  dataModel.load("foobar.xml");
9128  </code></pre>
9129  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9130  * <br><br>
9131  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9132  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9133  * 
9134  * Note: old style constructor is still suported (container, template, config)
9135  * 
9136  * @constructor
9137  * Create a new View
9138  * @param {Object} config The config object
9139  * 
9140  */
9141 Roo.View = function(config, depreciated_tpl, depreciated_config){
9142     
9143     if (typeof(depreciated_tpl) == 'undefined') {
9144         // new way.. - universal constructor.
9145         Roo.apply(this, config);
9146         this.el  = Roo.get(this.el);
9147     } else {
9148         // old format..
9149         this.el  = Roo.get(config);
9150         this.tpl = depreciated_tpl;
9151         Roo.apply(this, depreciated_config);
9152     }
9153      
9154     
9155     if(typeof(this.tpl) == "string"){
9156         this.tpl = new Roo.Template(this.tpl);
9157     } else {
9158         // support xtype ctors..
9159         this.tpl = new Roo.factory(this.tpl, Roo);
9160     }
9161     
9162     
9163     this.tpl.compile();
9164    
9165
9166      
9167     /** @private */
9168     this.addEvents({
9169         /**
9170          * @event beforeclick
9171          * Fires before a click is processed. Returns false to cancel the default action.
9172          * @param {Roo.View} this
9173          * @param {Number} index The index of the target node
9174          * @param {HTMLElement} node The target node
9175          * @param {Roo.EventObject} e The raw event object
9176          */
9177             "beforeclick" : true,
9178         /**
9179          * @event click
9180          * Fires when a template node is clicked.
9181          * @param {Roo.View} this
9182          * @param {Number} index The index of the target node
9183          * @param {HTMLElement} node The target node
9184          * @param {Roo.EventObject} e The raw event object
9185          */
9186             "click" : true,
9187         /**
9188          * @event dblclick
9189          * Fires when a template node is double clicked.
9190          * @param {Roo.View} this
9191          * @param {Number} index The index of the target node
9192          * @param {HTMLElement} node The target node
9193          * @param {Roo.EventObject} e The raw event object
9194          */
9195             "dblclick" : true,
9196         /**
9197          * @event contextmenu
9198          * Fires when a template node is right clicked.
9199          * @param {Roo.View} this
9200          * @param {Number} index The index of the target node
9201          * @param {HTMLElement} node The target node
9202          * @param {Roo.EventObject} e The raw event object
9203          */
9204             "contextmenu" : true,
9205         /**
9206          * @event selectionchange
9207          * Fires when the selected nodes change.
9208          * @param {Roo.View} this
9209          * @param {Array} selections Array of the selected nodes
9210          */
9211             "selectionchange" : true,
9212     
9213         /**
9214          * @event beforeselect
9215          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9216          * @param {Roo.View} this
9217          * @param {HTMLElement} node The node to be selected
9218          * @param {Array} selections Array of currently selected nodes
9219          */
9220             "beforeselect" : true,
9221         /**
9222          * @event preparedata
9223          * Fires on every row to render, to allow you to change the data.
9224          * @param {Roo.View} this
9225          * @param {Object} data to be rendered (change this)
9226          */
9227           "preparedata" : true
9228         });
9229
9230     this.el.on({
9231         "click": this.onClick,
9232         "dblclick": this.onDblClick,
9233         "contextmenu": this.onContextMenu,
9234         scope:this
9235     });
9236
9237     this.selections = [];
9238     this.nodes = [];
9239     this.cmp = new Roo.CompositeElementLite([]);
9240     if(this.store){
9241         this.store = Roo.factory(this.store, Roo.data);
9242         this.setStore(this.store, true);
9243     }
9244     Roo.View.superclass.constructor.call(this);
9245 };
9246
9247 Roo.extend(Roo.View, Roo.util.Observable, {
9248     
9249      /**
9250      * @cfg {Roo.data.Store} store Data store to load data from.
9251      */
9252     store : false,
9253     
9254     /**
9255      * @cfg {String|Roo.Element} el The container element.
9256      */
9257     el : '',
9258     
9259     /**
9260      * @cfg {String|Roo.Template} tpl The template used by this View 
9261      */
9262     tpl : false,
9263     /**
9264      * @cfg {String} dataName the named area of the template to use as the data area
9265      *                          Works with domtemplates roo-name="name"
9266      */
9267     dataName: false,
9268     /**
9269      * @cfg {String} selectedClass The css class to add to selected nodes
9270      */
9271     selectedClass : "x-view-selected",
9272      /**
9273      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9274      */
9275     emptyText : "",
9276     /**
9277      * @cfg {Boolean} multiSelect Allow multiple selection
9278      */
9279     multiSelect : false,
9280     /**
9281      * @cfg {Boolean} singleSelect Allow single selection
9282      */
9283     singleSelect:  false,
9284     
9285     /**
9286      * @cfg {Boolean} toggleSelect - selecting 
9287      */
9288     toggleSelect : false,
9289     
9290     /**
9291      * Returns the element this view is bound to.
9292      * @return {Roo.Element}
9293      */
9294     getEl : function(){
9295         return this.el;
9296     },
9297
9298     /**
9299      * Refreshes the view.
9300      */
9301     refresh : function(){
9302         var t = this.tpl;
9303         
9304         // if we are using something like 'domtemplate', then
9305         // the what gets used is:
9306         // t.applySubtemplate(NAME, data, wrapping data..)
9307         // the outer template then get' applied with
9308         //     the store 'extra data'
9309         // and the body get's added to the
9310         //      roo-name="data" node?
9311         //      <span class='roo-tpl-{name}'></span> ?????
9312         
9313         
9314         
9315         this.clearSelections();
9316         this.el.update("");
9317         var html = [];
9318         var records = this.store.getRange();
9319         if(records.length < 1) {
9320             
9321             // is this valid??  = should it render a template??
9322             
9323             this.el.update(this.emptyText);
9324             return;
9325         }
9326         var el = this.el;
9327         if (this.dataName) {
9328             this.el.update(t.apply(this.store.meta)); //????
9329             el = this.el.child('.roo-tpl-' + this.dataName);
9330         }
9331         
9332         for(var i = 0, len = records.length; i < len; i++){
9333             var data = this.prepareData(records[i].data, i, records[i]);
9334             this.fireEvent("preparedata", this, data, i, records[i]);
9335             html[html.length] = Roo.util.Format.trim(
9336                 this.dataName ?
9337                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9338                     t.apply(data)
9339             );
9340         }
9341         
9342         
9343         
9344         el.update(html.join(""));
9345         this.nodes = el.dom.childNodes;
9346         this.updateIndexes(0);
9347     },
9348
9349     /**
9350      * Function to override to reformat the data that is sent to
9351      * the template for each node.
9352      * DEPRICATED - use the preparedata event handler.
9353      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9354      * a JSON object for an UpdateManager bound view).
9355      */
9356     prepareData : function(data, index, record)
9357     {
9358         this.fireEvent("preparedata", this, data, index, record);
9359         return data;
9360     },
9361
9362     onUpdate : function(ds, record){
9363         this.clearSelections();
9364         var index = this.store.indexOf(record);
9365         var n = this.nodes[index];
9366         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9367         n.parentNode.removeChild(n);
9368         this.updateIndexes(index, index);
9369     },
9370
9371     
9372     
9373 // --------- FIXME     
9374     onAdd : function(ds, records, index)
9375     {
9376         this.clearSelections();
9377         if(this.nodes.length == 0){
9378             this.refresh();
9379             return;
9380         }
9381         var n = this.nodes[index];
9382         for(var i = 0, len = records.length; i < len; i++){
9383             var d = this.prepareData(records[i].data, i, records[i]);
9384             if(n){
9385                 this.tpl.insertBefore(n, d);
9386             }else{
9387                 
9388                 this.tpl.append(this.el, d);
9389             }
9390         }
9391         this.updateIndexes(index);
9392     },
9393
9394     onRemove : function(ds, record, index){
9395         this.clearSelections();
9396         var el = this.dataName  ?
9397             this.el.child('.roo-tpl-' + this.dataName) :
9398             this.el; 
9399         el.dom.removeChild(this.nodes[index]);
9400         this.updateIndexes(index);
9401     },
9402
9403     /**
9404      * Refresh an individual node.
9405      * @param {Number} index
9406      */
9407     refreshNode : function(index){
9408         this.onUpdate(this.store, this.store.getAt(index));
9409     },
9410
9411     updateIndexes : function(startIndex, endIndex){
9412         var ns = this.nodes;
9413         startIndex = startIndex || 0;
9414         endIndex = endIndex || ns.length - 1;
9415         for(var i = startIndex; i <= endIndex; i++){
9416             ns[i].nodeIndex = i;
9417         }
9418     },
9419
9420     /**
9421      * Changes the data store this view uses and refresh the view.
9422      * @param {Store} store
9423      */
9424     setStore : function(store, initial){
9425         if(!initial && this.store){
9426             this.store.un("datachanged", this.refresh);
9427             this.store.un("add", this.onAdd);
9428             this.store.un("remove", this.onRemove);
9429             this.store.un("update", this.onUpdate);
9430             this.store.un("clear", this.refresh);
9431         }
9432         if(store){
9433           
9434             store.on("datachanged", this.refresh, this);
9435             store.on("add", this.onAdd, this);
9436             store.on("remove", this.onRemove, this);
9437             store.on("update", this.onUpdate, this);
9438             store.on("clear", this.refresh, this);
9439         }
9440         
9441         if(store){
9442             this.refresh();
9443         }
9444     },
9445
9446     /**
9447      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9448      * @param {HTMLElement} node
9449      * @return {HTMLElement} The template node
9450      */
9451     findItemFromChild : function(node){
9452         var el = this.dataName  ?
9453             this.el.child('.roo-tpl-' + this.dataName,true) :
9454             this.el.dom; 
9455         
9456         if(!node || node.parentNode == el){
9457                     return node;
9458             }
9459             var p = node.parentNode;
9460             while(p && p != el){
9461             if(p.parentNode == el){
9462                 return p;
9463             }
9464             p = p.parentNode;
9465         }
9466             return null;
9467     },
9468
9469     /** @ignore */
9470     onClick : function(e){
9471         var item = this.findItemFromChild(e.getTarget());
9472         if(item){
9473             var index = this.indexOf(item);
9474             if(this.onItemClick(item, index, e) !== false){
9475                 this.fireEvent("click", this, index, item, e);
9476             }
9477         }else{
9478             this.clearSelections();
9479         }
9480     },
9481
9482     /** @ignore */
9483     onContextMenu : function(e){
9484         var item = this.findItemFromChild(e.getTarget());
9485         if(item){
9486             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9487         }
9488     },
9489
9490     /** @ignore */
9491     onDblClick : function(e){
9492         var item = this.findItemFromChild(e.getTarget());
9493         if(item){
9494             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9495         }
9496     },
9497
9498     onItemClick : function(item, index, e)
9499     {
9500         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9501             return false;
9502         }
9503         if (this.toggleSelect) {
9504             var m = this.isSelected(item) ? 'unselect' : 'select';
9505             Roo.log(m);
9506             var _t = this;
9507             _t[m](item, true, false);
9508             return true;
9509         }
9510         if(this.multiSelect || this.singleSelect){
9511             if(this.multiSelect && e.shiftKey && this.lastSelection){
9512                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9513             }else{
9514                 this.select(item, this.multiSelect && e.ctrlKey);
9515                 this.lastSelection = item;
9516             }
9517             e.preventDefault();
9518         }
9519         return true;
9520     },
9521
9522     /**
9523      * Get the number of selected nodes.
9524      * @return {Number}
9525      */
9526     getSelectionCount : function(){
9527         return this.selections.length;
9528     },
9529
9530     /**
9531      * Get the currently selected nodes.
9532      * @return {Array} An array of HTMLElements
9533      */
9534     getSelectedNodes : function(){
9535         return this.selections;
9536     },
9537
9538     /**
9539      * Get the indexes of the selected nodes.
9540      * @return {Array}
9541      */
9542     getSelectedIndexes : function(){
9543         var indexes = [], s = this.selections;
9544         for(var i = 0, len = s.length; i < len; i++){
9545             indexes.push(s[i].nodeIndex);
9546         }
9547         return indexes;
9548     },
9549
9550     /**
9551      * Clear all selections
9552      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9553      */
9554     clearSelections : function(suppressEvent){
9555         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9556             this.cmp.elements = this.selections;
9557             this.cmp.removeClass(this.selectedClass);
9558             this.selections = [];
9559             if(!suppressEvent){
9560                 this.fireEvent("selectionchange", this, this.selections);
9561             }
9562         }
9563     },
9564
9565     /**
9566      * Returns true if the passed node is selected
9567      * @param {HTMLElement/Number} node The node or node index
9568      * @return {Boolean}
9569      */
9570     isSelected : function(node){
9571         var s = this.selections;
9572         if(s.length < 1){
9573             return false;
9574         }
9575         node = this.getNode(node);
9576         return s.indexOf(node) !== -1;
9577     },
9578
9579     /**
9580      * Selects nodes.
9581      * @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
9582      * @param {Boolean} keepExisting (optional) true to keep existing selections
9583      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9584      */
9585     select : function(nodeInfo, keepExisting, suppressEvent){
9586         if(nodeInfo instanceof Array){
9587             if(!keepExisting){
9588                 this.clearSelections(true);
9589             }
9590             for(var i = 0, len = nodeInfo.length; i < len; i++){
9591                 this.select(nodeInfo[i], true, true);
9592             }
9593             return;
9594         } 
9595         var node = this.getNode(nodeInfo);
9596         if(!node || this.isSelected(node)){
9597             return; // already selected.
9598         }
9599         if(!keepExisting){
9600             this.clearSelections(true);
9601         }
9602         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9603             Roo.fly(node).addClass(this.selectedClass);
9604             this.selections.push(node);
9605             if(!suppressEvent){
9606                 this.fireEvent("selectionchange", this, this.selections);
9607             }
9608         }
9609         
9610         
9611     },
9612       /**
9613      * Unselects nodes.
9614      * @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
9615      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9616      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9617      */
9618     unselect : function(nodeInfo, keepExisting, suppressEvent)
9619     {
9620         if(nodeInfo instanceof Array){
9621             Roo.each(this.selections, function(s) {
9622                 this.unselect(s, nodeInfo);
9623             }, this);
9624             return;
9625         }
9626         var node = this.getNode(nodeInfo);
9627         if(!node || !this.isSelected(node)){
9628             Roo.log("not selected");
9629             return; // not selected.
9630         }
9631         // fireevent???
9632         var ns = [];
9633         Roo.each(this.selections, function(s) {
9634             if (s == node ) {
9635                 Roo.fly(node).removeClass(this.selectedClass);
9636
9637                 return;
9638             }
9639             ns.push(s);
9640         },this);
9641         
9642         this.selections= ns;
9643         this.fireEvent("selectionchange", this, this.selections);
9644     },
9645
9646     /**
9647      * Gets a template node.
9648      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9649      * @return {HTMLElement} The node or null if it wasn't found
9650      */
9651     getNode : function(nodeInfo){
9652         if(typeof nodeInfo == "string"){
9653             return document.getElementById(nodeInfo);
9654         }else if(typeof nodeInfo == "number"){
9655             return this.nodes[nodeInfo];
9656         }
9657         return nodeInfo;
9658     },
9659
9660     /**
9661      * Gets a range template nodes.
9662      * @param {Number} startIndex
9663      * @param {Number} endIndex
9664      * @return {Array} An array of nodes
9665      */
9666     getNodes : function(start, end){
9667         var ns = this.nodes;
9668         start = start || 0;
9669         end = typeof end == "undefined" ? ns.length - 1 : end;
9670         var nodes = [];
9671         if(start <= end){
9672             for(var i = start; i <= end; i++){
9673                 nodes.push(ns[i]);
9674             }
9675         } else{
9676             for(var i = start; i >= end; i--){
9677                 nodes.push(ns[i]);
9678             }
9679         }
9680         return nodes;
9681     },
9682
9683     /**
9684      * Finds the index of the passed node
9685      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9686      * @return {Number} The index of the node or -1
9687      */
9688     indexOf : function(node){
9689         node = this.getNode(node);
9690         if(typeof node.nodeIndex == "number"){
9691             return node.nodeIndex;
9692         }
9693         var ns = this.nodes;
9694         for(var i = 0, len = ns.length; i < len; i++){
9695             if(ns[i] == node){
9696                 return i;
9697             }
9698         }
9699         return -1;
9700     }
9701 });
9702 /*
9703  * Based on:
9704  * Ext JS Library 1.1.1
9705  * Copyright(c) 2006-2007, Ext JS, LLC.
9706  *
9707  * Originally Released Under LGPL - original licence link has changed is not relivant.
9708  *
9709  * Fork - LGPL
9710  * <script type="text/javascript">
9711  */
9712
9713 /**
9714  * @class Roo.JsonView
9715  * @extends Roo.View
9716  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9717 <pre><code>
9718 var view = new Roo.JsonView({
9719     container: "my-element",
9720     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9721     multiSelect: true, 
9722     jsonRoot: "data" 
9723 });
9724
9725 // listen for node click?
9726 view.on("click", function(vw, index, node, e){
9727     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9728 });
9729
9730 // direct load of JSON data
9731 view.load("foobar.php");
9732
9733 // Example from my blog list
9734 var tpl = new Roo.Template(
9735     '&lt;div class="entry"&gt;' +
9736     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9737     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9738     "&lt;/div&gt;&lt;hr /&gt;"
9739 );
9740
9741 var moreView = new Roo.JsonView({
9742     container :  "entry-list", 
9743     template : tpl,
9744     jsonRoot: "posts"
9745 });
9746 moreView.on("beforerender", this.sortEntries, this);
9747 moreView.load({
9748     url: "/blog/get-posts.php",
9749     params: "allposts=true",
9750     text: "Loading Blog Entries..."
9751 });
9752 </code></pre>
9753
9754 * Note: old code is supported with arguments : (container, template, config)
9755
9756
9757  * @constructor
9758  * Create a new JsonView
9759  * 
9760  * @param {Object} config The config object
9761  * 
9762  */
9763 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9764     
9765     
9766     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9767
9768     var um = this.el.getUpdateManager();
9769     um.setRenderer(this);
9770     um.on("update", this.onLoad, this);
9771     um.on("failure", this.onLoadException, this);
9772
9773     /**
9774      * @event beforerender
9775      * Fires before rendering of the downloaded JSON data.
9776      * @param {Roo.JsonView} this
9777      * @param {Object} data The JSON data loaded
9778      */
9779     /**
9780      * @event load
9781      * Fires when data is loaded.
9782      * @param {Roo.JsonView} this
9783      * @param {Object} data The JSON data loaded
9784      * @param {Object} response The raw Connect response object
9785      */
9786     /**
9787      * @event loadexception
9788      * Fires when loading fails.
9789      * @param {Roo.JsonView} this
9790      * @param {Object} response The raw Connect response object
9791      */
9792     this.addEvents({
9793         'beforerender' : true,
9794         'load' : true,
9795         'loadexception' : true
9796     });
9797 };
9798 Roo.extend(Roo.JsonView, Roo.View, {
9799     /**
9800      * @type {String} The root property in the loaded JSON object that contains the data
9801      */
9802     jsonRoot : "",
9803
9804     /**
9805      * Refreshes the view.
9806      */
9807     refresh : function(){
9808         this.clearSelections();
9809         this.el.update("");
9810         var html = [];
9811         var o = this.jsonData;
9812         if(o && o.length > 0){
9813             for(var i = 0, len = o.length; i < len; i++){
9814                 var data = this.prepareData(o[i], i, o);
9815                 html[html.length] = this.tpl.apply(data);
9816             }
9817         }else{
9818             html.push(this.emptyText);
9819         }
9820         this.el.update(html.join(""));
9821         this.nodes = this.el.dom.childNodes;
9822         this.updateIndexes(0);
9823     },
9824
9825     /**
9826      * 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.
9827      * @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:
9828      <pre><code>
9829      view.load({
9830          url: "your-url.php",
9831          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9832          callback: yourFunction,
9833          scope: yourObject, //(optional scope)
9834          discardUrl: false,
9835          nocache: false,
9836          text: "Loading...",
9837          timeout: 30,
9838          scripts: false
9839      });
9840      </code></pre>
9841      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9842      * 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.
9843      * @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}
9844      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9845      * @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.
9846      */
9847     load : function(){
9848         var um = this.el.getUpdateManager();
9849         um.update.apply(um, arguments);
9850     },
9851
9852     render : function(el, response){
9853         this.clearSelections();
9854         this.el.update("");
9855         var o;
9856         try{
9857             o = Roo.util.JSON.decode(response.responseText);
9858             if(this.jsonRoot){
9859                 
9860                 o = o[this.jsonRoot];
9861             }
9862         } catch(e){
9863         }
9864         /**
9865          * The current JSON data or null
9866          */
9867         this.jsonData = o;
9868         this.beforeRender();
9869         this.refresh();
9870     },
9871
9872 /**
9873  * Get the number of records in the current JSON dataset
9874  * @return {Number}
9875  */
9876     getCount : function(){
9877         return this.jsonData ? this.jsonData.length : 0;
9878     },
9879
9880 /**
9881  * Returns the JSON object for the specified node(s)
9882  * @param {HTMLElement/Array} node The node or an array of nodes
9883  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9884  * you get the JSON object for the node
9885  */
9886     getNodeData : function(node){
9887         if(node instanceof Array){
9888             var data = [];
9889             for(var i = 0, len = node.length; i < len; i++){
9890                 data.push(this.getNodeData(node[i]));
9891             }
9892             return data;
9893         }
9894         return this.jsonData[this.indexOf(node)] || null;
9895     },
9896
9897     beforeRender : function(){
9898         this.snapshot = this.jsonData;
9899         if(this.sortInfo){
9900             this.sort.apply(this, this.sortInfo);
9901         }
9902         this.fireEvent("beforerender", this, this.jsonData);
9903     },
9904
9905     onLoad : function(el, o){
9906         this.fireEvent("load", this, this.jsonData, o);
9907     },
9908
9909     onLoadException : function(el, o){
9910         this.fireEvent("loadexception", this, o);
9911     },
9912
9913 /**
9914  * Filter the data by a specific property.
9915  * @param {String} property A property on your JSON objects
9916  * @param {String/RegExp} value Either string that the property values
9917  * should start with, or a RegExp to test against the property
9918  */
9919     filter : function(property, value){
9920         if(this.jsonData){
9921             var data = [];
9922             var ss = this.snapshot;
9923             if(typeof value == "string"){
9924                 var vlen = value.length;
9925                 if(vlen == 0){
9926                     this.clearFilter();
9927                     return;
9928                 }
9929                 value = value.toLowerCase();
9930                 for(var i = 0, len = ss.length; i < len; i++){
9931                     var o = ss[i];
9932                     if(o[property].substr(0, vlen).toLowerCase() == value){
9933                         data.push(o);
9934                     }
9935                 }
9936             } else if(value.exec){ // regex?
9937                 for(var i = 0, len = ss.length; i < len; i++){
9938                     var o = ss[i];
9939                     if(value.test(o[property])){
9940                         data.push(o);
9941                     }
9942                 }
9943             } else{
9944                 return;
9945             }
9946             this.jsonData = data;
9947             this.refresh();
9948         }
9949     },
9950
9951 /**
9952  * Filter by a function. The passed function will be called with each
9953  * object in the current dataset. If the function returns true the value is kept,
9954  * otherwise it is filtered.
9955  * @param {Function} fn
9956  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9957  */
9958     filterBy : function(fn, scope){
9959         if(this.jsonData){
9960             var data = [];
9961             var ss = this.snapshot;
9962             for(var i = 0, len = ss.length; i < len; i++){
9963                 var o = ss[i];
9964                 if(fn.call(scope || this, o)){
9965                     data.push(o);
9966                 }
9967             }
9968             this.jsonData = data;
9969             this.refresh();
9970         }
9971     },
9972
9973 /**
9974  * Clears the current filter.
9975  */
9976     clearFilter : function(){
9977         if(this.snapshot && this.jsonData != this.snapshot){
9978             this.jsonData = this.snapshot;
9979             this.refresh();
9980         }
9981     },
9982
9983
9984 /**
9985  * Sorts the data for this view and refreshes it.
9986  * @param {String} property A property on your JSON objects to sort on
9987  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9988  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9989  */
9990     sort : function(property, dir, sortType){
9991         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9992         if(this.jsonData){
9993             var p = property;
9994             var dsc = dir && dir.toLowerCase() == "desc";
9995             var f = function(o1, o2){
9996                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9997                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9998                 ;
9999                 if(v1 < v2){
10000                     return dsc ? +1 : -1;
10001                 } else if(v1 > v2){
10002                     return dsc ? -1 : +1;
10003                 } else{
10004                     return 0;
10005                 }
10006             };
10007             this.jsonData.sort(f);
10008             this.refresh();
10009             if(this.jsonData != this.snapshot){
10010                 this.snapshot.sort(f);
10011             }
10012         }
10013     }
10014 });/*
10015  * Based on:
10016  * Ext JS Library 1.1.1
10017  * Copyright(c) 2006-2007, Ext JS, LLC.
10018  *
10019  * Originally Released Under LGPL - original licence link has changed is not relivant.
10020  *
10021  * Fork - LGPL
10022  * <script type="text/javascript">
10023  */
10024  
10025
10026 /**
10027  * @class Roo.ColorPalette
10028  * @extends Roo.Component
10029  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
10030  * Here's an example of typical usage:
10031  * <pre><code>
10032 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
10033 cp.render('my-div');
10034
10035 cp.on('select', function(palette, selColor){
10036     // do something with selColor
10037 });
10038 </code></pre>
10039  * @constructor
10040  * Create a new ColorPalette
10041  * @param {Object} config The config object
10042  */
10043 Roo.ColorPalette = function(config){
10044     Roo.ColorPalette.superclass.constructor.call(this, config);
10045     this.addEvents({
10046         /**
10047              * @event select
10048              * Fires when a color is selected
10049              * @param {ColorPalette} this
10050              * @param {String} color The 6-digit color hex code (without the # symbol)
10051              */
10052         select: true
10053     });
10054
10055     if(this.handler){
10056         this.on("select", this.handler, this.scope, true);
10057     }
10058 };
10059 Roo.extend(Roo.ColorPalette, Roo.Component, {
10060     /**
10061      * @cfg {String} itemCls
10062      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10063      */
10064     itemCls : "x-color-palette",
10065     /**
10066      * @cfg {String} value
10067      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10068      * the hex codes are case-sensitive.
10069      */
10070     value : null,
10071     clickEvent:'click',
10072     // private
10073     ctype: "Roo.ColorPalette",
10074
10075     /**
10076      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10077      */
10078     allowReselect : false,
10079
10080     /**
10081      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10082      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10083      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10084      * of colors with the width setting until the box is symmetrical.</p>
10085      * <p>You can override individual colors if needed:</p>
10086      * <pre><code>
10087 var cp = new Roo.ColorPalette();
10088 cp.colors[0] = "FF0000";  // change the first box to red
10089 </code></pre>
10090
10091 Or you can provide a custom array of your own for complete control:
10092 <pre><code>
10093 var cp = new Roo.ColorPalette();
10094 cp.colors = ["000000", "993300", "333300"];
10095 </code></pre>
10096      * @type Array
10097      */
10098     colors : [
10099         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10100         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10101         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10102         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10103         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10104     ],
10105
10106     // private
10107     onRender : function(container, position){
10108         var t = new Roo.MasterTemplate(
10109             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10110         );
10111         var c = this.colors;
10112         for(var i = 0, len = c.length; i < len; i++){
10113             t.add([c[i]]);
10114         }
10115         var el = document.createElement("div");
10116         el.className = this.itemCls;
10117         t.overwrite(el);
10118         container.dom.insertBefore(el, position);
10119         this.el = Roo.get(el);
10120         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10121         if(this.clickEvent != 'click'){
10122             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10123         }
10124     },
10125
10126     // private
10127     afterRender : function(){
10128         Roo.ColorPalette.superclass.afterRender.call(this);
10129         if(this.value){
10130             var s = this.value;
10131             this.value = null;
10132             this.select(s);
10133         }
10134     },
10135
10136     // private
10137     handleClick : function(e, t){
10138         e.preventDefault();
10139         if(!this.disabled){
10140             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10141             this.select(c.toUpperCase());
10142         }
10143     },
10144
10145     /**
10146      * Selects the specified color in the palette (fires the select event)
10147      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10148      */
10149     select : function(color){
10150         color = color.replace("#", "");
10151         if(color != this.value || this.allowReselect){
10152             var el = this.el;
10153             if(this.value){
10154                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10155             }
10156             el.child("a.color-"+color).addClass("x-color-palette-sel");
10157             this.value = color;
10158             this.fireEvent("select", this, color);
10159         }
10160     }
10161 });/*
10162  * Based on:
10163  * Ext JS Library 1.1.1
10164  * Copyright(c) 2006-2007, Ext JS, LLC.
10165  *
10166  * Originally Released Under LGPL - original licence link has changed is not relivant.
10167  *
10168  * Fork - LGPL
10169  * <script type="text/javascript">
10170  */
10171  
10172 /**
10173  * @class Roo.DatePicker
10174  * @extends Roo.Component
10175  * Simple date picker class.
10176  * @constructor
10177  * Create a new DatePicker
10178  * @param {Object} config The config object
10179  */
10180 Roo.DatePicker = function(config){
10181     Roo.DatePicker.superclass.constructor.call(this, config);
10182
10183     this.value = config && config.value ?
10184                  config.value.clearTime() : new Date().clearTime();
10185
10186     this.addEvents({
10187         /**
10188              * @event select
10189              * Fires when a date is selected
10190              * @param {DatePicker} this
10191              * @param {Date} date The selected date
10192              */
10193         'select': true,
10194         /**
10195              * @event monthchange
10196              * Fires when the displayed month changes 
10197              * @param {DatePicker} this
10198              * @param {Date} date The selected month
10199              */
10200         'monthchange': true
10201     });
10202
10203     if(this.handler){
10204         this.on("select", this.handler,  this.scope || this);
10205     }
10206     // build the disabledDatesRE
10207     if(!this.disabledDatesRE && this.disabledDates){
10208         var dd = this.disabledDates;
10209         var re = "(?:";
10210         for(var i = 0; i < dd.length; i++){
10211             re += dd[i];
10212             if(i != dd.length-1) re += "|";
10213         }
10214         this.disabledDatesRE = new RegExp(re + ")");
10215     }
10216 };
10217
10218 Roo.extend(Roo.DatePicker, Roo.Component, {
10219     /**
10220      * @cfg {String} todayText
10221      * The text to display on the button that selects the current date (defaults to "Today")
10222      */
10223     todayText : "Today",
10224     /**
10225      * @cfg {String} okText
10226      * The text to display on the ok button
10227      */
10228     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10229     /**
10230      * @cfg {String} cancelText
10231      * The text to display on the cancel button
10232      */
10233     cancelText : "Cancel",
10234     /**
10235      * @cfg {String} todayTip
10236      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10237      */
10238     todayTip : "{0} (Spacebar)",
10239     /**
10240      * @cfg {Date} minDate
10241      * Minimum allowable date (JavaScript date object, defaults to null)
10242      */
10243     minDate : null,
10244     /**
10245      * @cfg {Date} maxDate
10246      * Maximum allowable date (JavaScript date object, defaults to null)
10247      */
10248     maxDate : null,
10249     /**
10250      * @cfg {String} minText
10251      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10252      */
10253     minText : "This date is before the minimum date",
10254     /**
10255      * @cfg {String} maxText
10256      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10257      */
10258     maxText : "This date is after the maximum date",
10259     /**
10260      * @cfg {String} format
10261      * The default date format string which can be overriden for localization support.  The format must be
10262      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10263      */
10264     format : "m/d/y",
10265     /**
10266      * @cfg {Array} disabledDays
10267      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10268      */
10269     disabledDays : null,
10270     /**
10271      * @cfg {String} disabledDaysText
10272      * The tooltip to display when the date falls on a disabled day (defaults to "")
10273      */
10274     disabledDaysText : "",
10275     /**
10276      * @cfg {RegExp} disabledDatesRE
10277      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10278      */
10279     disabledDatesRE : null,
10280     /**
10281      * @cfg {String} disabledDatesText
10282      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10283      */
10284     disabledDatesText : "",
10285     /**
10286      * @cfg {Boolean} constrainToViewport
10287      * True to constrain the date picker to the viewport (defaults to true)
10288      */
10289     constrainToViewport : true,
10290     /**
10291      * @cfg {Array} monthNames
10292      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10293      */
10294     monthNames : Date.monthNames,
10295     /**
10296      * @cfg {Array} dayNames
10297      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10298      */
10299     dayNames : Date.dayNames,
10300     /**
10301      * @cfg {String} nextText
10302      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10303      */
10304     nextText: 'Next Month (Control+Right)',
10305     /**
10306      * @cfg {String} prevText
10307      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10308      */
10309     prevText: 'Previous Month (Control+Left)',
10310     /**
10311      * @cfg {String} monthYearText
10312      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10313      */
10314     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10315     /**
10316      * @cfg {Number} startDay
10317      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10318      */
10319     startDay : 0,
10320     /**
10321      * @cfg {Bool} showClear
10322      * Show a clear button (usefull for date form elements that can be blank.)
10323      */
10324     
10325     showClear: false,
10326     
10327     /**
10328      * Sets the value of the date field
10329      * @param {Date} value The date to set
10330      */
10331     setValue : function(value){
10332         var old = this.value;
10333         this.value = value.clearTime(true);
10334         if(this.el){
10335             this.update(this.value);
10336         }
10337     },
10338
10339     /**
10340      * Gets the current selected value of the date field
10341      * @return {Date} The selected date
10342      */
10343     getValue : function(){
10344         return this.value;
10345     },
10346
10347     // private
10348     focus : function(){
10349         if(this.el){
10350             this.update(this.activeDate);
10351         }
10352     },
10353
10354     // private
10355     onRender : function(container, position){
10356         var m = [
10357              '<table cellspacing="0">',
10358                 '<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>',
10359                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10360         var dn = this.dayNames;
10361         for(var i = 0; i < 7; i++){
10362             var d = this.startDay+i;
10363             if(d > 6){
10364                 d = d-7;
10365             }
10366             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10367         }
10368         m[m.length] = "</tr></thead><tbody><tr>";
10369         for(var i = 0; i < 42; i++) {
10370             if(i % 7 == 0 && i != 0){
10371                 m[m.length] = "</tr><tr>";
10372             }
10373             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10374         }
10375         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10376             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10377
10378         var el = document.createElement("div");
10379         el.className = "x-date-picker";
10380         el.innerHTML = m.join("");
10381
10382         container.dom.insertBefore(el, position);
10383
10384         this.el = Roo.get(el);
10385         this.eventEl = Roo.get(el.firstChild);
10386
10387         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10388             handler: this.showPrevMonth,
10389             scope: this,
10390             preventDefault:true,
10391             stopDefault:true
10392         });
10393
10394         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10395             handler: this.showNextMonth,
10396             scope: this,
10397             preventDefault:true,
10398             stopDefault:true
10399         });
10400
10401         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10402
10403         this.monthPicker = this.el.down('div.x-date-mp');
10404         this.monthPicker.enableDisplayMode('block');
10405         
10406         var kn = new Roo.KeyNav(this.eventEl, {
10407             "left" : function(e){
10408                 e.ctrlKey ?
10409                     this.showPrevMonth() :
10410                     this.update(this.activeDate.add("d", -1));
10411             },
10412
10413             "right" : function(e){
10414                 e.ctrlKey ?
10415                     this.showNextMonth() :
10416                     this.update(this.activeDate.add("d", 1));
10417             },
10418
10419             "up" : function(e){
10420                 e.ctrlKey ?
10421                     this.showNextYear() :
10422                     this.update(this.activeDate.add("d", -7));
10423             },
10424
10425             "down" : function(e){
10426                 e.ctrlKey ?
10427                     this.showPrevYear() :
10428                     this.update(this.activeDate.add("d", 7));
10429             },
10430
10431             "pageUp" : function(e){
10432                 this.showNextMonth();
10433             },
10434
10435             "pageDown" : function(e){
10436                 this.showPrevMonth();
10437             },
10438
10439             "enter" : function(e){
10440                 e.stopPropagation();
10441                 return true;
10442             },
10443
10444             scope : this
10445         });
10446
10447         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10448
10449         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10450
10451         this.el.unselectable();
10452         
10453         this.cells = this.el.select("table.x-date-inner tbody td");
10454         this.textNodes = this.el.query("table.x-date-inner tbody span");
10455
10456         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10457             text: "&#160;",
10458             tooltip: this.monthYearText
10459         });
10460
10461         this.mbtn.on('click', this.showMonthPicker, this);
10462         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10463
10464
10465         var today = (new Date()).dateFormat(this.format);
10466         
10467         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10468         if (this.showClear) {
10469             baseTb.add( new Roo.Toolbar.Fill());
10470         }
10471         baseTb.add({
10472             text: String.format(this.todayText, today),
10473             tooltip: String.format(this.todayTip, today),
10474             handler: this.selectToday,
10475             scope: this
10476         });
10477         
10478         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10479             
10480         //});
10481         if (this.showClear) {
10482             
10483             baseTb.add( new Roo.Toolbar.Fill());
10484             baseTb.add({
10485                 text: '&#160;',
10486                 cls: 'x-btn-icon x-btn-clear',
10487                 handler: function() {
10488                     //this.value = '';
10489                     this.fireEvent("select", this, '');
10490                 },
10491                 scope: this
10492             });
10493         }
10494         
10495         
10496         if(Roo.isIE){
10497             this.el.repaint();
10498         }
10499         this.update(this.value);
10500     },
10501
10502     createMonthPicker : function(){
10503         if(!this.monthPicker.dom.firstChild){
10504             var buf = ['<table border="0" cellspacing="0">'];
10505             for(var i = 0; i < 6; i++){
10506                 buf.push(
10507                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10508                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10509                     i == 0 ?
10510                     '<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>' :
10511                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10512                 );
10513             }
10514             buf.push(
10515                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10516                     this.okText,
10517                     '</button><button type="button" class="x-date-mp-cancel">',
10518                     this.cancelText,
10519                     '</button></td></tr>',
10520                 '</table>'
10521             );
10522             this.monthPicker.update(buf.join(''));
10523             this.monthPicker.on('click', this.onMonthClick, this);
10524             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10525
10526             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10527             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10528
10529             this.mpMonths.each(function(m, a, i){
10530                 i += 1;
10531                 if((i%2) == 0){
10532                     m.dom.xmonth = 5 + Math.round(i * .5);
10533                 }else{
10534                     m.dom.xmonth = Math.round((i-1) * .5);
10535                 }
10536             });
10537         }
10538     },
10539
10540     showMonthPicker : function(){
10541         this.createMonthPicker();
10542         var size = this.el.getSize();
10543         this.monthPicker.setSize(size);
10544         this.monthPicker.child('table').setSize(size);
10545
10546         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10547         this.updateMPMonth(this.mpSelMonth);
10548         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10549         this.updateMPYear(this.mpSelYear);
10550
10551         this.monthPicker.slideIn('t', {duration:.2});
10552     },
10553
10554     updateMPYear : function(y){
10555         this.mpyear = y;
10556         var ys = this.mpYears.elements;
10557         for(var i = 1; i <= 10; i++){
10558             var td = ys[i-1], y2;
10559             if((i%2) == 0){
10560                 y2 = y + Math.round(i * .5);
10561                 td.firstChild.innerHTML = y2;
10562                 td.xyear = y2;
10563             }else{
10564                 y2 = y - (5-Math.round(i * .5));
10565                 td.firstChild.innerHTML = y2;
10566                 td.xyear = y2;
10567             }
10568             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10569         }
10570     },
10571
10572     updateMPMonth : function(sm){
10573         this.mpMonths.each(function(m, a, i){
10574             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10575         });
10576     },
10577
10578     selectMPMonth: function(m){
10579         
10580     },
10581
10582     onMonthClick : function(e, t){
10583         e.stopEvent();
10584         var el = new Roo.Element(t), pn;
10585         if(el.is('button.x-date-mp-cancel')){
10586             this.hideMonthPicker();
10587         }
10588         else if(el.is('button.x-date-mp-ok')){
10589             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10590             this.hideMonthPicker();
10591         }
10592         else if(pn = el.up('td.x-date-mp-month', 2)){
10593             this.mpMonths.removeClass('x-date-mp-sel');
10594             pn.addClass('x-date-mp-sel');
10595             this.mpSelMonth = pn.dom.xmonth;
10596         }
10597         else if(pn = el.up('td.x-date-mp-year', 2)){
10598             this.mpYears.removeClass('x-date-mp-sel');
10599             pn.addClass('x-date-mp-sel');
10600             this.mpSelYear = pn.dom.xyear;
10601         }
10602         else if(el.is('a.x-date-mp-prev')){
10603             this.updateMPYear(this.mpyear-10);
10604         }
10605         else if(el.is('a.x-date-mp-next')){
10606             this.updateMPYear(this.mpyear+10);
10607         }
10608     },
10609
10610     onMonthDblClick : function(e, t){
10611         e.stopEvent();
10612         var el = new Roo.Element(t), pn;
10613         if(pn = el.up('td.x-date-mp-month', 2)){
10614             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10615             this.hideMonthPicker();
10616         }
10617         else if(pn = el.up('td.x-date-mp-year', 2)){
10618             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10619             this.hideMonthPicker();
10620         }
10621     },
10622
10623     hideMonthPicker : function(disableAnim){
10624         if(this.monthPicker){
10625             if(disableAnim === true){
10626                 this.monthPicker.hide();
10627             }else{
10628                 this.monthPicker.slideOut('t', {duration:.2});
10629             }
10630         }
10631     },
10632
10633     // private
10634     showPrevMonth : function(e){
10635         this.update(this.activeDate.add("mo", -1));
10636     },
10637
10638     // private
10639     showNextMonth : function(e){
10640         this.update(this.activeDate.add("mo", 1));
10641     },
10642
10643     // private
10644     showPrevYear : function(){
10645         this.update(this.activeDate.add("y", -1));
10646     },
10647
10648     // private
10649     showNextYear : function(){
10650         this.update(this.activeDate.add("y", 1));
10651     },
10652
10653     // private
10654     handleMouseWheel : function(e){
10655         var delta = e.getWheelDelta();
10656         if(delta > 0){
10657             this.showPrevMonth();
10658             e.stopEvent();
10659         } else if(delta < 0){
10660             this.showNextMonth();
10661             e.stopEvent();
10662         }
10663     },
10664
10665     // private
10666     handleDateClick : function(e, t){
10667         e.stopEvent();
10668         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10669             this.setValue(new Date(t.dateValue));
10670             this.fireEvent("select", this, this.value);
10671         }
10672     },
10673
10674     // private
10675     selectToday : function(){
10676         this.setValue(new Date().clearTime());
10677         this.fireEvent("select", this, this.value);
10678     },
10679
10680     // private
10681     update : function(date)
10682     {
10683         var vd = this.activeDate;
10684         this.activeDate = date;
10685         if(vd && this.el){
10686             var t = date.getTime();
10687             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10688                 this.cells.removeClass("x-date-selected");
10689                 this.cells.each(function(c){
10690                    if(c.dom.firstChild.dateValue == t){
10691                        c.addClass("x-date-selected");
10692                        setTimeout(function(){
10693                             try{c.dom.firstChild.focus();}catch(e){}
10694                        }, 50);
10695                        return false;
10696                    }
10697                 });
10698                 return;
10699             }
10700         }
10701         
10702         var days = date.getDaysInMonth();
10703         var firstOfMonth = date.getFirstDateOfMonth();
10704         var startingPos = firstOfMonth.getDay()-this.startDay;
10705
10706         if(startingPos <= this.startDay){
10707             startingPos += 7;
10708         }
10709
10710         var pm = date.add("mo", -1);
10711         var prevStart = pm.getDaysInMonth()-startingPos;
10712
10713         var cells = this.cells.elements;
10714         var textEls = this.textNodes;
10715         days += startingPos;
10716
10717         // convert everything to numbers so it's fast
10718         var day = 86400000;
10719         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10720         var today = new Date().clearTime().getTime();
10721         var sel = date.clearTime().getTime();
10722         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10723         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10724         var ddMatch = this.disabledDatesRE;
10725         var ddText = this.disabledDatesText;
10726         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10727         var ddaysText = this.disabledDaysText;
10728         var format = this.format;
10729
10730         var setCellClass = function(cal, cell){
10731             cell.title = "";
10732             var t = d.getTime();
10733             cell.firstChild.dateValue = t;
10734             if(t == today){
10735                 cell.className += " x-date-today";
10736                 cell.title = cal.todayText;
10737             }
10738             if(t == sel){
10739                 cell.className += " x-date-selected";
10740                 setTimeout(function(){
10741                     try{cell.firstChild.focus();}catch(e){}
10742                 }, 50);
10743             }
10744             // disabling
10745             if(t < min) {
10746                 cell.className = " x-date-disabled";
10747                 cell.title = cal.minText;
10748                 return;
10749             }
10750             if(t > max) {
10751                 cell.className = " x-date-disabled";
10752                 cell.title = cal.maxText;
10753                 return;
10754             }
10755             if(ddays){
10756                 if(ddays.indexOf(d.getDay()) != -1){
10757                     cell.title = ddaysText;
10758                     cell.className = " x-date-disabled";
10759                 }
10760             }
10761             if(ddMatch && format){
10762                 var fvalue = d.dateFormat(format);
10763                 if(ddMatch.test(fvalue)){
10764                     cell.title = ddText.replace("%0", fvalue);
10765                     cell.className = " x-date-disabled";
10766                 }
10767             }
10768         };
10769
10770         var i = 0;
10771         for(; i < startingPos; i++) {
10772             textEls[i].innerHTML = (++prevStart);
10773             d.setDate(d.getDate()+1);
10774             cells[i].className = "x-date-prevday";
10775             setCellClass(this, cells[i]);
10776         }
10777         for(; i < days; i++){
10778             intDay = i - startingPos + 1;
10779             textEls[i].innerHTML = (intDay);
10780             d.setDate(d.getDate()+1);
10781             cells[i].className = "x-date-active";
10782             setCellClass(this, cells[i]);
10783         }
10784         var extraDays = 0;
10785         for(; i < 42; i++) {
10786              textEls[i].innerHTML = (++extraDays);
10787              d.setDate(d.getDate()+1);
10788              cells[i].className = "x-date-nextday";
10789              setCellClass(this, cells[i]);
10790         }
10791
10792         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10793         this.fireEvent('monthchange', this, date);
10794         
10795         if(!this.internalRender){
10796             var main = this.el.dom.firstChild;
10797             var w = main.offsetWidth;
10798             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10799             Roo.fly(main).setWidth(w);
10800             this.internalRender = true;
10801             // opera does not respect the auto grow header center column
10802             // then, after it gets a width opera refuses to recalculate
10803             // without a second pass
10804             if(Roo.isOpera && !this.secondPass){
10805                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10806                 this.secondPass = true;
10807                 this.update.defer(10, this, [date]);
10808             }
10809         }
10810         
10811         
10812     }
10813 });        /*
10814  * Based on:
10815  * Ext JS Library 1.1.1
10816  * Copyright(c) 2006-2007, Ext JS, LLC.
10817  *
10818  * Originally Released Under LGPL - original licence link has changed is not relivant.
10819  *
10820  * Fork - LGPL
10821  * <script type="text/javascript">
10822  */
10823 /**
10824  * @class Roo.TabPanel
10825  * @extends Roo.util.Observable
10826  * A lightweight tab container.
10827  * <br><br>
10828  * Usage:
10829  * <pre><code>
10830 // basic tabs 1, built from existing content
10831 var tabs = new Roo.TabPanel("tabs1");
10832 tabs.addTab("script", "View Script");
10833 tabs.addTab("markup", "View Markup");
10834 tabs.activate("script");
10835
10836 // more advanced tabs, built from javascript
10837 var jtabs = new Roo.TabPanel("jtabs");
10838 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10839
10840 // set up the UpdateManager
10841 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10842 var updater = tab2.getUpdateManager();
10843 updater.setDefaultUrl("ajax1.htm");
10844 tab2.on('activate', updater.refresh, updater, true);
10845
10846 // Use setUrl for Ajax loading
10847 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10848 tab3.setUrl("ajax2.htm", null, true);
10849
10850 // Disabled tab
10851 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10852 tab4.disable();
10853
10854 jtabs.activate("jtabs-1");
10855  * </code></pre>
10856  * @constructor
10857  * Create a new TabPanel.
10858  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10859  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10860  */
10861 Roo.TabPanel = function(container, config){
10862     /**
10863     * The container element for this TabPanel.
10864     * @type Roo.Element
10865     */
10866     this.el = Roo.get(container, true);
10867     if(config){
10868         if(typeof config == "boolean"){
10869             this.tabPosition = config ? "bottom" : "top";
10870         }else{
10871             Roo.apply(this, config);
10872         }
10873     }
10874     if(this.tabPosition == "bottom"){
10875         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10876         this.el.addClass("x-tabs-bottom");
10877     }
10878     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10879     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10880     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10881     if(Roo.isIE){
10882         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10883     }
10884     if(this.tabPosition != "bottom"){
10885         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10886          * @type Roo.Element
10887          */
10888         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10889         this.el.addClass("x-tabs-top");
10890     }
10891     this.items = [];
10892
10893     this.bodyEl.setStyle("position", "relative");
10894
10895     this.active = null;
10896     this.activateDelegate = this.activate.createDelegate(this);
10897
10898     this.addEvents({
10899         /**
10900          * @event tabchange
10901          * Fires when the active tab changes
10902          * @param {Roo.TabPanel} this
10903          * @param {Roo.TabPanelItem} activePanel The new active tab
10904          */
10905         "tabchange": true,
10906         /**
10907          * @event beforetabchange
10908          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10909          * @param {Roo.TabPanel} this
10910          * @param {Object} e Set cancel to true on this object to cancel the tab change
10911          * @param {Roo.TabPanelItem} tab The tab being changed to
10912          */
10913         "beforetabchange" : true
10914     });
10915
10916     Roo.EventManager.onWindowResize(this.onResize, this);
10917     this.cpad = this.el.getPadding("lr");
10918     this.hiddenCount = 0;
10919
10920
10921     // toolbar on the tabbar support...
10922     if (this.toolbar) {
10923         var tcfg = this.toolbar;
10924         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10925         this.toolbar = new Roo.Toolbar(tcfg);
10926         if (Roo.isSafari) {
10927             var tbl = tcfg.container.child('table', true);
10928             tbl.setAttribute('width', '100%');
10929         }
10930         
10931     }
10932    
10933
10934
10935     Roo.TabPanel.superclass.constructor.call(this);
10936 };
10937
10938 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10939     /*
10940      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10941      */
10942     tabPosition : "top",
10943     /*
10944      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10945      */
10946     currentTabWidth : 0,
10947     /*
10948      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10949      */
10950     minTabWidth : 40,
10951     /*
10952      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10953      */
10954     maxTabWidth : 250,
10955     /*
10956      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10957      */
10958     preferredTabWidth : 175,
10959     /*
10960      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10961      */
10962     resizeTabs : false,
10963     /*
10964      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10965      */
10966     monitorResize : true,
10967     /*
10968      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10969      */
10970     toolbar : false,
10971
10972     /**
10973      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10974      * @param {String} id The id of the div to use <b>or create</b>
10975      * @param {String} text The text for the tab
10976      * @param {String} content (optional) Content to put in the TabPanelItem body
10977      * @param {Boolean} closable (optional) True to create a close icon on the tab
10978      * @return {Roo.TabPanelItem} The created TabPanelItem
10979      */
10980     addTab : function(id, text, content, closable){
10981         var item = new Roo.TabPanelItem(this, id, text, closable);
10982         this.addTabItem(item);
10983         if(content){
10984             item.setContent(content);
10985         }
10986         return item;
10987     },
10988
10989     /**
10990      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10991      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10992      * @return {Roo.TabPanelItem}
10993      */
10994     getTab : function(id){
10995         return this.items[id];
10996     },
10997
10998     /**
10999      * Hides the {@link Roo.TabPanelItem} with the specified id/index
11000      * @param {String/Number} id The id or index of the TabPanelItem to hide.
11001      */
11002     hideTab : function(id){
11003         var t = this.items[id];
11004         if(!t.isHidden()){
11005            t.setHidden(true);
11006            this.hiddenCount++;
11007            this.autoSizeTabs();
11008         }
11009     },
11010
11011     /**
11012      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
11013      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
11014      */
11015     unhideTab : function(id){
11016         var t = this.items[id];
11017         if(t.isHidden()){
11018            t.setHidden(false);
11019            this.hiddenCount--;
11020            this.autoSizeTabs();
11021         }
11022     },
11023
11024     /**
11025      * Adds an existing {@link Roo.TabPanelItem}.
11026      * @param {Roo.TabPanelItem} item The TabPanelItem to add
11027      */
11028     addTabItem : function(item){
11029         this.items[item.id] = item;
11030         this.items.push(item);
11031         if(this.resizeTabs){
11032            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
11033            this.autoSizeTabs();
11034         }else{
11035             item.autoSize();
11036         }
11037     },
11038
11039     /**
11040      * Removes a {@link Roo.TabPanelItem}.
11041      * @param {String/Number} id The id or index of the TabPanelItem to remove.
11042      */
11043     removeTab : function(id){
11044         var items = this.items;
11045         var tab = items[id];
11046         if(!tab) { return; }
11047         var index = items.indexOf(tab);
11048         if(this.active == tab && items.length > 1){
11049             var newTab = this.getNextAvailable(index);
11050             if(newTab) {
11051                 newTab.activate();
11052             }
11053         }
11054         this.stripEl.dom.removeChild(tab.pnode.dom);
11055         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11056             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11057         }
11058         items.splice(index, 1);
11059         delete this.items[tab.id];
11060         tab.fireEvent("close", tab);
11061         tab.purgeListeners();
11062         this.autoSizeTabs();
11063     },
11064
11065     getNextAvailable : function(start){
11066         var items = this.items;
11067         var index = start;
11068         // look for a next tab that will slide over to
11069         // replace the one being removed
11070         while(index < items.length){
11071             var item = items[++index];
11072             if(item && !item.isHidden()){
11073                 return item;
11074             }
11075         }
11076         // if one isn't found select the previous tab (on the left)
11077         index = start;
11078         while(index >= 0){
11079             var item = items[--index];
11080             if(item && !item.isHidden()){
11081                 return item;
11082             }
11083         }
11084         return null;
11085     },
11086
11087     /**
11088      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11089      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11090      */
11091     disableTab : function(id){
11092         var tab = this.items[id];
11093         if(tab && this.active != tab){
11094             tab.disable();
11095         }
11096     },
11097
11098     /**
11099      * Enables a {@link Roo.TabPanelItem} that is disabled.
11100      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11101      */
11102     enableTab : function(id){
11103         var tab = this.items[id];
11104         tab.enable();
11105     },
11106
11107     /**
11108      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11109      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11110      * @return {Roo.TabPanelItem} The TabPanelItem.
11111      */
11112     activate : function(id){
11113         var tab = this.items[id];
11114         if(!tab){
11115             return null;
11116         }
11117         if(tab == this.active || tab.disabled){
11118             return tab;
11119         }
11120         var e = {};
11121         this.fireEvent("beforetabchange", this, e, tab);
11122         if(e.cancel !== true && !tab.disabled){
11123             if(this.active){
11124                 this.active.hide();
11125             }
11126             this.active = this.items[id];
11127             this.active.show();
11128             this.fireEvent("tabchange", this, this.active);
11129         }
11130         return tab;
11131     },
11132
11133     /**
11134      * Gets the active {@link Roo.TabPanelItem}.
11135      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11136      */
11137     getActiveTab : function(){
11138         return this.active;
11139     },
11140
11141     /**
11142      * Updates the tab body element to fit the height of the container element
11143      * for overflow scrolling
11144      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11145      */
11146     syncHeight : function(targetHeight){
11147         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11148         var bm = this.bodyEl.getMargins();
11149         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11150         this.bodyEl.setHeight(newHeight);
11151         return newHeight;
11152     },
11153
11154     onResize : function(){
11155         if(this.monitorResize){
11156             this.autoSizeTabs();
11157         }
11158     },
11159
11160     /**
11161      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11162      */
11163     beginUpdate : function(){
11164         this.updating = true;
11165     },
11166
11167     /**
11168      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11169      */
11170     endUpdate : function(){
11171         this.updating = false;
11172         this.autoSizeTabs();
11173     },
11174
11175     /**
11176      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11177      */
11178     autoSizeTabs : function(){
11179         var count = this.items.length;
11180         var vcount = count - this.hiddenCount;
11181         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11182         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11183         var availWidth = Math.floor(w / vcount);
11184         var b = this.stripBody;
11185         if(b.getWidth() > w){
11186             var tabs = this.items;
11187             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11188             if(availWidth < this.minTabWidth){
11189                 /*if(!this.sleft){    // incomplete scrolling code
11190                     this.createScrollButtons();
11191                 }
11192                 this.showScroll();
11193                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11194             }
11195         }else{
11196             if(this.currentTabWidth < this.preferredTabWidth){
11197                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11198             }
11199         }
11200     },
11201
11202     /**
11203      * Returns the number of tabs in this TabPanel.
11204      * @return {Number}
11205      */
11206      getCount : function(){
11207          return this.items.length;
11208      },
11209
11210     /**
11211      * Resizes all the tabs to the passed width
11212      * @param {Number} The new width
11213      */
11214     setTabWidth : function(width){
11215         this.currentTabWidth = width;
11216         for(var i = 0, len = this.items.length; i < len; i++) {
11217                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11218         }
11219     },
11220
11221     /**
11222      * Destroys this TabPanel
11223      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11224      */
11225     destroy : function(removeEl){
11226         Roo.EventManager.removeResizeListener(this.onResize, this);
11227         for(var i = 0, len = this.items.length; i < len; i++){
11228             this.items[i].purgeListeners();
11229         }
11230         if(removeEl === true){
11231             this.el.update("");
11232             this.el.remove();
11233         }
11234     }
11235 });
11236
11237 /**
11238  * @class Roo.TabPanelItem
11239  * @extends Roo.util.Observable
11240  * Represents an individual item (tab plus body) in a TabPanel.
11241  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11242  * @param {String} id The id of this TabPanelItem
11243  * @param {String} text The text for the tab of this TabPanelItem
11244  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11245  */
11246 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11247     /**
11248      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11249      * @type Roo.TabPanel
11250      */
11251     this.tabPanel = tabPanel;
11252     /**
11253      * The id for this TabPanelItem
11254      * @type String
11255      */
11256     this.id = id;
11257     /** @private */
11258     this.disabled = false;
11259     /** @private */
11260     this.text = text;
11261     /** @private */
11262     this.loaded = false;
11263     this.closable = closable;
11264
11265     /**
11266      * The body element for this TabPanelItem.
11267      * @type Roo.Element
11268      */
11269     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11270     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11271     this.bodyEl.setStyle("display", "block");
11272     this.bodyEl.setStyle("zoom", "1");
11273     this.hideAction();
11274
11275     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11276     /** @private */
11277     this.el = Roo.get(els.el, true);
11278     this.inner = Roo.get(els.inner, true);
11279     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11280     this.pnode = Roo.get(els.el.parentNode, true);
11281     this.el.on("mousedown", this.onTabMouseDown, this);
11282     this.el.on("click", this.onTabClick, this);
11283     /** @private */
11284     if(closable){
11285         var c = Roo.get(els.close, true);
11286         c.dom.title = this.closeText;
11287         c.addClassOnOver("close-over");
11288         c.on("click", this.closeClick, this);
11289      }
11290
11291     this.addEvents({
11292          /**
11293          * @event activate
11294          * Fires when this tab becomes the active tab.
11295          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11296          * @param {Roo.TabPanelItem} this
11297          */
11298         "activate": true,
11299         /**
11300          * @event beforeclose
11301          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11302          * @param {Roo.TabPanelItem} this
11303          * @param {Object} e Set cancel to true on this object to cancel the close.
11304          */
11305         "beforeclose": true,
11306         /**
11307          * @event close
11308          * Fires when this tab is closed.
11309          * @param {Roo.TabPanelItem} this
11310          */
11311          "close": true,
11312         /**
11313          * @event deactivate
11314          * Fires when this tab is no longer the active tab.
11315          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11316          * @param {Roo.TabPanelItem} this
11317          */
11318          "deactivate" : true
11319     });
11320     this.hidden = false;
11321
11322     Roo.TabPanelItem.superclass.constructor.call(this);
11323 };
11324
11325 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11326     purgeListeners : function(){
11327        Roo.util.Observable.prototype.purgeListeners.call(this);
11328        this.el.removeAllListeners();
11329     },
11330     /**
11331      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11332      */
11333     show : function(){
11334         this.pnode.addClass("on");
11335         this.showAction();
11336         if(Roo.isOpera){
11337             this.tabPanel.stripWrap.repaint();
11338         }
11339         this.fireEvent("activate", this.tabPanel, this);
11340     },
11341
11342     /**
11343      * Returns true if this tab is the active tab.
11344      * @return {Boolean}
11345      */
11346     isActive : function(){
11347         return this.tabPanel.getActiveTab() == this;
11348     },
11349
11350     /**
11351      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11352      */
11353     hide : function(){
11354         this.pnode.removeClass("on");
11355         this.hideAction();
11356         this.fireEvent("deactivate", this.tabPanel, this);
11357     },
11358
11359     hideAction : function(){
11360         this.bodyEl.hide();
11361         this.bodyEl.setStyle("position", "absolute");
11362         this.bodyEl.setLeft("-20000px");
11363         this.bodyEl.setTop("-20000px");
11364     },
11365
11366     showAction : function(){
11367         this.bodyEl.setStyle("position", "relative");
11368         this.bodyEl.setTop("");
11369         this.bodyEl.setLeft("");
11370         this.bodyEl.show();
11371     },
11372
11373     /**
11374      * Set the tooltip for the tab.
11375      * @param {String} tooltip The tab's tooltip
11376      */
11377     setTooltip : function(text){
11378         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11379             this.textEl.dom.qtip = text;
11380             this.textEl.dom.removeAttribute('title');
11381         }else{
11382             this.textEl.dom.title = text;
11383         }
11384     },
11385
11386     onTabClick : function(e){
11387         e.preventDefault();
11388         this.tabPanel.activate(this.id);
11389     },
11390
11391     onTabMouseDown : function(e){
11392         e.preventDefault();
11393         this.tabPanel.activate(this.id);
11394     },
11395
11396     getWidth : function(){
11397         return this.inner.getWidth();
11398     },
11399
11400     setWidth : function(width){
11401         var iwidth = width - this.pnode.getPadding("lr");
11402         this.inner.setWidth(iwidth);
11403         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11404         this.pnode.setWidth(width);
11405     },
11406
11407     /**
11408      * Show or hide the tab
11409      * @param {Boolean} hidden True to hide or false to show.
11410      */
11411     setHidden : function(hidden){
11412         this.hidden = hidden;
11413         this.pnode.setStyle("display", hidden ? "none" : "");
11414     },
11415
11416     /**
11417      * Returns true if this tab is "hidden"
11418      * @return {Boolean}
11419      */
11420     isHidden : function(){
11421         return this.hidden;
11422     },
11423
11424     /**
11425      * Returns the text for this tab
11426      * @return {String}
11427      */
11428     getText : function(){
11429         return this.text;
11430     },
11431
11432     autoSize : function(){
11433         //this.el.beginMeasure();
11434         this.textEl.setWidth(1);
11435         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11436         //this.el.endMeasure();
11437     },
11438
11439     /**
11440      * Sets the text for the tab (Note: this also sets the tooltip text)
11441      * @param {String} text The tab's text and tooltip
11442      */
11443     setText : function(text){
11444         this.text = text;
11445         this.textEl.update(text);
11446         this.setTooltip(text);
11447         if(!this.tabPanel.resizeTabs){
11448             this.autoSize();
11449         }
11450     },
11451     /**
11452      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11453      */
11454     activate : function(){
11455         this.tabPanel.activate(this.id);
11456     },
11457
11458     /**
11459      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11460      */
11461     disable : function(){
11462         if(this.tabPanel.active != this){
11463             this.disabled = true;
11464             this.pnode.addClass("disabled");
11465         }
11466     },
11467
11468     /**
11469      * Enables this TabPanelItem if it was previously disabled.
11470      */
11471     enable : function(){
11472         this.disabled = false;
11473         this.pnode.removeClass("disabled");
11474     },
11475
11476     /**
11477      * Sets the content for this TabPanelItem.
11478      * @param {String} content The content
11479      * @param {Boolean} loadScripts true to look for and load scripts
11480      */
11481     setContent : function(content, loadScripts){
11482         this.bodyEl.update(content, loadScripts);
11483     },
11484
11485     /**
11486      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11487      * @return {Roo.UpdateManager} The UpdateManager
11488      */
11489     getUpdateManager : function(){
11490         return this.bodyEl.getUpdateManager();
11491     },
11492
11493     /**
11494      * Set a URL to be used to load the content for this TabPanelItem.
11495      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11496      * @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)
11497      * @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)
11498      * @return {Roo.UpdateManager} The UpdateManager
11499      */
11500     setUrl : function(url, params, loadOnce){
11501         if(this.refreshDelegate){
11502             this.un('activate', this.refreshDelegate);
11503         }
11504         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11505         this.on("activate", this.refreshDelegate);
11506         return this.bodyEl.getUpdateManager();
11507     },
11508
11509     /** @private */
11510     _handleRefresh : function(url, params, loadOnce){
11511         if(!loadOnce || !this.loaded){
11512             var updater = this.bodyEl.getUpdateManager();
11513             updater.update(url, params, this._setLoaded.createDelegate(this));
11514         }
11515     },
11516
11517     /**
11518      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11519      *   Will fail silently if the setUrl method has not been called.
11520      *   This does not activate the panel, just updates its content.
11521      */
11522     refresh : function(){
11523         if(this.refreshDelegate){
11524            this.loaded = false;
11525            this.refreshDelegate();
11526         }
11527     },
11528
11529     /** @private */
11530     _setLoaded : function(){
11531         this.loaded = true;
11532     },
11533
11534     /** @private */
11535     closeClick : function(e){
11536         var o = {};
11537         e.stopEvent();
11538         this.fireEvent("beforeclose", this, o);
11539         if(o.cancel !== true){
11540             this.tabPanel.removeTab(this.id);
11541         }
11542     },
11543     /**
11544      * The text displayed in the tooltip for the close icon.
11545      * @type String
11546      */
11547     closeText : "Close this tab"
11548 });
11549
11550 /** @private */
11551 Roo.TabPanel.prototype.createStrip = function(container){
11552     var strip = document.createElement("div");
11553     strip.className = "x-tabs-wrap";
11554     container.appendChild(strip);
11555     return strip;
11556 };
11557 /** @private */
11558 Roo.TabPanel.prototype.createStripList = function(strip){
11559     // div wrapper for retard IE
11560     // returns the "tr" element.
11561     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11562         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11563         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11564     return strip.firstChild.firstChild.firstChild.firstChild;
11565 };
11566 /** @private */
11567 Roo.TabPanel.prototype.createBody = function(container){
11568     var body = document.createElement("div");
11569     Roo.id(body, "tab-body");
11570     Roo.fly(body).addClass("x-tabs-body");
11571     container.appendChild(body);
11572     return body;
11573 };
11574 /** @private */
11575 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11576     var body = Roo.getDom(id);
11577     if(!body){
11578         body = document.createElement("div");
11579         body.id = id;
11580     }
11581     Roo.fly(body).addClass("x-tabs-item-body");
11582     bodyEl.insertBefore(body, bodyEl.firstChild);
11583     return body;
11584 };
11585 /** @private */
11586 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11587     var td = document.createElement("td");
11588     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11589     //stripEl.appendChild(td);
11590     if(closable){
11591         td.className = "x-tabs-closable";
11592         if(!this.closeTpl){
11593             this.closeTpl = new Roo.Template(
11594                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11595                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11596                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11597             );
11598         }
11599         var el = this.closeTpl.overwrite(td, {"text": text});
11600         var close = el.getElementsByTagName("div")[0];
11601         var inner = el.getElementsByTagName("em")[0];
11602         return {"el": el, "close": close, "inner": inner};
11603     } else {
11604         if(!this.tabTpl){
11605             this.tabTpl = new Roo.Template(
11606                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11607                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11608             );
11609         }
11610         var el = this.tabTpl.overwrite(td, {"text": text});
11611         var inner = el.getElementsByTagName("em")[0];
11612         return {"el": el, "inner": inner};
11613     }
11614 };/*
11615  * Based on:
11616  * Ext JS Library 1.1.1
11617  * Copyright(c) 2006-2007, Ext JS, LLC.
11618  *
11619  * Originally Released Under LGPL - original licence link has changed is not relivant.
11620  *
11621  * Fork - LGPL
11622  * <script type="text/javascript">
11623  */
11624
11625 /**
11626  * @class Roo.Button
11627  * @extends Roo.util.Observable
11628  * Simple Button class
11629  * @cfg {String} text The button text
11630  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11631  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11632  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11633  * @cfg {Object} scope The scope of the handler
11634  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11635  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11636  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11637  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11638  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11639  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11640    applies if enableToggle = true)
11641  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11642  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11643   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11644  * @constructor
11645  * Create a new button
11646  * @param {Object} config The config object
11647  */
11648 Roo.Button = function(renderTo, config)
11649 {
11650     if (!config) {
11651         config = renderTo;
11652         renderTo = config.renderTo || false;
11653     }
11654     
11655     Roo.apply(this, config);
11656     this.addEvents({
11657         /**
11658              * @event click
11659              * Fires when this button is clicked
11660              * @param {Button} this
11661              * @param {EventObject} e The click event
11662              */
11663             "click" : true,
11664         /**
11665              * @event toggle
11666              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11667              * @param {Button} this
11668              * @param {Boolean} pressed
11669              */
11670             "toggle" : true,
11671         /**
11672              * @event mouseover
11673              * Fires when the mouse hovers over the button
11674              * @param {Button} this
11675              * @param {Event} e The event object
11676              */
11677         'mouseover' : true,
11678         /**
11679              * @event mouseout
11680              * Fires when the mouse exits the button
11681              * @param {Button} this
11682              * @param {Event} e The event object
11683              */
11684         'mouseout': true,
11685          /**
11686              * @event render
11687              * Fires when the button is rendered
11688              * @param {Button} this
11689              */
11690         'render': true
11691     });
11692     if(this.menu){
11693         this.menu = Roo.menu.MenuMgr.get(this.menu);
11694     }
11695     // register listeners first!!  - so render can be captured..
11696     Roo.util.Observable.call(this);
11697     if(renderTo){
11698         this.render(renderTo);
11699     }
11700     
11701   
11702 };
11703
11704 Roo.extend(Roo.Button, Roo.util.Observable, {
11705     /**
11706      * 
11707      */
11708     
11709     /**
11710      * Read-only. True if this button is hidden
11711      * @type Boolean
11712      */
11713     hidden : false,
11714     /**
11715      * Read-only. True if this button is disabled
11716      * @type Boolean
11717      */
11718     disabled : false,
11719     /**
11720      * Read-only. True if this button is pressed (only if enableToggle = true)
11721      * @type Boolean
11722      */
11723     pressed : false,
11724
11725     /**
11726      * @cfg {Number} tabIndex 
11727      * The DOM tabIndex for this button (defaults to undefined)
11728      */
11729     tabIndex : undefined,
11730
11731     /**
11732      * @cfg {Boolean} enableToggle
11733      * True to enable pressed/not pressed toggling (defaults to false)
11734      */
11735     enableToggle: false,
11736     /**
11737      * @cfg {Mixed} menu
11738      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11739      */
11740     menu : undefined,
11741     /**
11742      * @cfg {String} menuAlign
11743      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11744      */
11745     menuAlign : "tl-bl?",
11746
11747     /**
11748      * @cfg {String} iconCls
11749      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11750      */
11751     iconCls : undefined,
11752     /**
11753      * @cfg {String} type
11754      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11755      */
11756     type : 'button',
11757
11758     // private
11759     menuClassTarget: 'tr',
11760
11761     /**
11762      * @cfg {String} clickEvent
11763      * The type of event to map to the button's event handler (defaults to 'click')
11764      */
11765     clickEvent : 'click',
11766
11767     /**
11768      * @cfg {Boolean} handleMouseEvents
11769      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11770      */
11771     handleMouseEvents : true,
11772
11773     /**
11774      * @cfg {String} tooltipType
11775      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11776      */
11777     tooltipType : 'qtip',
11778
11779     /**
11780      * @cfg {String} cls
11781      * A CSS class to apply to the button's main element.
11782      */
11783     
11784     /**
11785      * @cfg {Roo.Template} template (Optional)
11786      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11787      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11788      * require code modifications if required elements (e.g. a button) aren't present.
11789      */
11790
11791     // private
11792     render : function(renderTo){
11793         var btn;
11794         if(this.hideParent){
11795             this.parentEl = Roo.get(renderTo);
11796         }
11797         if(!this.dhconfig){
11798             if(!this.template){
11799                 if(!Roo.Button.buttonTemplate){
11800                     // hideous table template
11801                     Roo.Button.buttonTemplate = new Roo.Template(
11802                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11803                         '<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>',
11804                         "</tr></tbody></table>");
11805                 }
11806                 this.template = Roo.Button.buttonTemplate;
11807             }
11808             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11809             var btnEl = btn.child("button:first");
11810             btnEl.on('focus', this.onFocus, this);
11811             btnEl.on('blur', this.onBlur, this);
11812             if(this.cls){
11813                 btn.addClass(this.cls);
11814             }
11815             if(this.icon){
11816                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11817             }
11818             if(this.iconCls){
11819                 btnEl.addClass(this.iconCls);
11820                 if(!this.cls){
11821                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11822                 }
11823             }
11824             if(this.tabIndex !== undefined){
11825                 btnEl.dom.tabIndex = this.tabIndex;
11826             }
11827             if(this.tooltip){
11828                 if(typeof this.tooltip == 'object'){
11829                     Roo.QuickTips.tips(Roo.apply({
11830                           target: btnEl.id
11831                     }, this.tooltip));
11832                 } else {
11833                     btnEl.dom[this.tooltipType] = this.tooltip;
11834                 }
11835             }
11836         }else{
11837             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11838         }
11839         this.el = btn;
11840         if(this.id){
11841             this.el.dom.id = this.el.id = this.id;
11842         }
11843         if(this.menu){
11844             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11845             this.menu.on("show", this.onMenuShow, this);
11846             this.menu.on("hide", this.onMenuHide, this);
11847         }
11848         btn.addClass("x-btn");
11849         if(Roo.isIE && !Roo.isIE7){
11850             this.autoWidth.defer(1, this);
11851         }else{
11852             this.autoWidth();
11853         }
11854         if(this.handleMouseEvents){
11855             btn.on("mouseover", this.onMouseOver, this);
11856             btn.on("mouseout", this.onMouseOut, this);
11857             btn.on("mousedown", this.onMouseDown, this);
11858         }
11859         btn.on(this.clickEvent, this.onClick, this);
11860         //btn.on("mouseup", this.onMouseUp, this);
11861         if(this.hidden){
11862             this.hide();
11863         }
11864         if(this.disabled){
11865             this.disable();
11866         }
11867         Roo.ButtonToggleMgr.register(this);
11868         if(this.pressed){
11869             this.el.addClass("x-btn-pressed");
11870         }
11871         if(this.repeat){
11872             var repeater = new Roo.util.ClickRepeater(btn,
11873                 typeof this.repeat == "object" ? this.repeat : {}
11874             );
11875             repeater.on("click", this.onClick,  this);
11876         }
11877         
11878         this.fireEvent('render', this);
11879         
11880     },
11881     /**
11882      * Returns the button's underlying element
11883      * @return {Roo.Element} The element
11884      */
11885     getEl : function(){
11886         return this.el;  
11887     },
11888     
11889     /**
11890      * Destroys this Button and removes any listeners.
11891      */
11892     destroy : function(){
11893         Roo.ButtonToggleMgr.unregister(this);
11894         this.el.removeAllListeners();
11895         this.purgeListeners();
11896         this.el.remove();
11897     },
11898
11899     // private
11900     autoWidth : function(){
11901         if(this.el){
11902             this.el.setWidth("auto");
11903             if(Roo.isIE7 && Roo.isStrict){
11904                 var ib = this.el.child('button');
11905                 if(ib && ib.getWidth() > 20){
11906                     ib.clip();
11907                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11908                 }
11909             }
11910             if(this.minWidth){
11911                 if(this.hidden){
11912                     this.el.beginMeasure();
11913                 }
11914                 if(this.el.getWidth() < this.minWidth){
11915                     this.el.setWidth(this.minWidth);
11916                 }
11917                 if(this.hidden){
11918                     this.el.endMeasure();
11919                 }
11920             }
11921         }
11922     },
11923
11924     /**
11925      * Assigns this button's click handler
11926      * @param {Function} handler The function to call when the button is clicked
11927      * @param {Object} scope (optional) Scope for the function passed in
11928      */
11929     setHandler : function(handler, scope){
11930         this.handler = handler;
11931         this.scope = scope;  
11932     },
11933     
11934     /**
11935      * Sets this button's text
11936      * @param {String} text The button text
11937      */
11938     setText : function(text){
11939         this.text = text;
11940         if(this.el){
11941             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11942         }
11943         this.autoWidth();
11944     },
11945     
11946     /**
11947      * Gets the text for this button
11948      * @return {String} The button text
11949      */
11950     getText : function(){
11951         return this.text;  
11952     },
11953     
11954     /**
11955      * Show this button
11956      */
11957     show: function(){
11958         this.hidden = false;
11959         if(this.el){
11960             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11961         }
11962     },
11963     
11964     /**
11965      * Hide this button
11966      */
11967     hide: function(){
11968         this.hidden = true;
11969         if(this.el){
11970             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11971         }
11972     },
11973     
11974     /**
11975      * Convenience function for boolean show/hide
11976      * @param {Boolean} visible True to show, false to hide
11977      */
11978     setVisible: function(visible){
11979         if(visible) {
11980             this.show();
11981         }else{
11982             this.hide();
11983         }
11984     },
11985     
11986     /**
11987      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11988      * @param {Boolean} state (optional) Force a particular state
11989      */
11990     toggle : function(state){
11991         state = state === undefined ? !this.pressed : state;
11992         if(state != this.pressed){
11993             if(state){
11994                 this.el.addClass("x-btn-pressed");
11995                 this.pressed = true;
11996                 this.fireEvent("toggle", this, true);
11997             }else{
11998                 this.el.removeClass("x-btn-pressed");
11999                 this.pressed = false;
12000                 this.fireEvent("toggle", this, false);
12001             }
12002             if(this.toggleHandler){
12003                 this.toggleHandler.call(this.scope || this, this, state);
12004             }
12005         }
12006     },
12007     
12008     /**
12009      * Focus the button
12010      */
12011     focus : function(){
12012         this.el.child('button:first').focus();
12013     },
12014     
12015     /**
12016      * Disable this button
12017      */
12018     disable : function(){
12019         if(this.el){
12020             this.el.addClass("x-btn-disabled");
12021         }
12022         this.disabled = true;
12023     },
12024     
12025     /**
12026      * Enable this button
12027      */
12028     enable : function(){
12029         if(this.el){
12030             this.el.removeClass("x-btn-disabled");
12031         }
12032         this.disabled = false;
12033     },
12034
12035     /**
12036      * Convenience function for boolean enable/disable
12037      * @param {Boolean} enabled True to enable, false to disable
12038      */
12039     setDisabled : function(v){
12040         this[v !== true ? "enable" : "disable"]();
12041     },
12042
12043     // private
12044     onClick : function(e){
12045         if(e){
12046             e.preventDefault();
12047         }
12048         if(e.button != 0){
12049             return;
12050         }
12051         if(!this.disabled){
12052             if(this.enableToggle){
12053                 this.toggle();
12054             }
12055             if(this.menu && !this.menu.isVisible()){
12056                 this.menu.show(this.el, this.menuAlign);
12057             }
12058             this.fireEvent("click", this, e);
12059             if(this.handler){
12060                 this.el.removeClass("x-btn-over");
12061                 this.handler.call(this.scope || this, this, e);
12062             }
12063         }
12064     },
12065     // private
12066     onMouseOver : function(e){
12067         if(!this.disabled){
12068             this.el.addClass("x-btn-over");
12069             this.fireEvent('mouseover', this, e);
12070         }
12071     },
12072     // private
12073     onMouseOut : function(e){
12074         if(!e.within(this.el,  true)){
12075             this.el.removeClass("x-btn-over");
12076             this.fireEvent('mouseout', this, e);
12077         }
12078     },
12079     // private
12080     onFocus : function(e){
12081         if(!this.disabled){
12082             this.el.addClass("x-btn-focus");
12083         }
12084     },
12085     // private
12086     onBlur : function(e){
12087         this.el.removeClass("x-btn-focus");
12088     },
12089     // private
12090     onMouseDown : function(e){
12091         if(!this.disabled && e.button == 0){
12092             this.el.addClass("x-btn-click");
12093             Roo.get(document).on('mouseup', this.onMouseUp, this);
12094         }
12095     },
12096     // private
12097     onMouseUp : function(e){
12098         if(e.button == 0){
12099             this.el.removeClass("x-btn-click");
12100             Roo.get(document).un('mouseup', this.onMouseUp, this);
12101         }
12102     },
12103     // private
12104     onMenuShow : function(e){
12105         this.el.addClass("x-btn-menu-active");
12106     },
12107     // private
12108     onMenuHide : function(e){
12109         this.el.removeClass("x-btn-menu-active");
12110     }   
12111 });
12112
12113 // Private utility class used by Button
12114 Roo.ButtonToggleMgr = function(){
12115    var groups = {};
12116    
12117    function toggleGroup(btn, state){
12118        if(state){
12119            var g = groups[btn.toggleGroup];
12120            for(var i = 0, l = g.length; i < l; i++){
12121                if(g[i] != btn){
12122                    g[i].toggle(false);
12123                }
12124            }
12125        }
12126    }
12127    
12128    return {
12129        register : function(btn){
12130            if(!btn.toggleGroup){
12131                return;
12132            }
12133            var g = groups[btn.toggleGroup];
12134            if(!g){
12135                g = groups[btn.toggleGroup] = [];
12136            }
12137            g.push(btn);
12138            btn.on("toggle", toggleGroup);
12139        },
12140        
12141        unregister : function(btn){
12142            if(!btn.toggleGroup){
12143                return;
12144            }
12145            var g = groups[btn.toggleGroup];
12146            if(g){
12147                g.remove(btn);
12148                btn.un("toggle", toggleGroup);
12149            }
12150        }
12151    };
12152 }();/*
12153  * Based on:
12154  * Ext JS Library 1.1.1
12155  * Copyright(c) 2006-2007, Ext JS, LLC.
12156  *
12157  * Originally Released Under LGPL - original licence link has changed is not relivant.
12158  *
12159  * Fork - LGPL
12160  * <script type="text/javascript">
12161  */
12162  
12163 /**
12164  * @class Roo.SplitButton
12165  * @extends Roo.Button
12166  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12167  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12168  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12169  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12170  * @cfg {String} arrowTooltip The title attribute of the arrow
12171  * @constructor
12172  * Create a new menu button
12173  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12174  * @param {Object} config The config object
12175  */
12176 Roo.SplitButton = function(renderTo, config){
12177     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12178     /**
12179      * @event arrowclick
12180      * Fires when this button's arrow is clicked
12181      * @param {SplitButton} this
12182      * @param {EventObject} e The click event
12183      */
12184     this.addEvents({"arrowclick":true});
12185 };
12186
12187 Roo.extend(Roo.SplitButton, Roo.Button, {
12188     render : function(renderTo){
12189         // this is one sweet looking template!
12190         var tpl = new Roo.Template(
12191             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12192             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12193             '<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>',
12194             "</tbody></table></td><td>",
12195             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12196             '<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>',
12197             "</tbody></table></td></tr></table>"
12198         );
12199         var btn = tpl.append(renderTo, [this.text, this.type], true);
12200         var btnEl = btn.child("button");
12201         if(this.cls){
12202             btn.addClass(this.cls);
12203         }
12204         if(this.icon){
12205             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12206         }
12207         if(this.iconCls){
12208             btnEl.addClass(this.iconCls);
12209             if(!this.cls){
12210                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12211             }
12212         }
12213         this.el = btn;
12214         if(this.handleMouseEvents){
12215             btn.on("mouseover", this.onMouseOver, this);
12216             btn.on("mouseout", this.onMouseOut, this);
12217             btn.on("mousedown", this.onMouseDown, this);
12218             btn.on("mouseup", this.onMouseUp, this);
12219         }
12220         btn.on(this.clickEvent, this.onClick, this);
12221         if(this.tooltip){
12222             if(typeof this.tooltip == 'object'){
12223                 Roo.QuickTips.tips(Roo.apply({
12224                       target: btnEl.id
12225                 }, this.tooltip));
12226             } else {
12227                 btnEl.dom[this.tooltipType] = this.tooltip;
12228             }
12229         }
12230         if(this.arrowTooltip){
12231             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12232         }
12233         if(this.hidden){
12234             this.hide();
12235         }
12236         if(this.disabled){
12237             this.disable();
12238         }
12239         if(this.pressed){
12240             this.el.addClass("x-btn-pressed");
12241         }
12242         if(Roo.isIE && !Roo.isIE7){
12243             this.autoWidth.defer(1, this);
12244         }else{
12245             this.autoWidth();
12246         }
12247         if(this.menu){
12248             this.menu.on("show", this.onMenuShow, this);
12249             this.menu.on("hide", this.onMenuHide, this);
12250         }
12251         this.fireEvent('render', this);
12252     },
12253
12254     // private
12255     autoWidth : function(){
12256         if(this.el){
12257             var tbl = this.el.child("table:first");
12258             var tbl2 = this.el.child("table:last");
12259             this.el.setWidth("auto");
12260             tbl.setWidth("auto");
12261             if(Roo.isIE7 && Roo.isStrict){
12262                 var ib = this.el.child('button:first');
12263                 if(ib && ib.getWidth() > 20){
12264                     ib.clip();
12265                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12266                 }
12267             }
12268             if(this.minWidth){
12269                 if(this.hidden){
12270                     this.el.beginMeasure();
12271                 }
12272                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12273                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12274                 }
12275                 if(this.hidden){
12276                     this.el.endMeasure();
12277                 }
12278             }
12279             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12280         } 
12281     },
12282     /**
12283      * Sets this button's click handler
12284      * @param {Function} handler The function to call when the button is clicked
12285      * @param {Object} scope (optional) Scope for the function passed above
12286      */
12287     setHandler : function(handler, scope){
12288         this.handler = handler;
12289         this.scope = scope;  
12290     },
12291     
12292     /**
12293      * Sets this button's arrow click handler
12294      * @param {Function} handler The function to call when the arrow is clicked
12295      * @param {Object} scope (optional) Scope for the function passed above
12296      */
12297     setArrowHandler : function(handler, scope){
12298         this.arrowHandler = handler;
12299         this.scope = scope;  
12300     },
12301     
12302     /**
12303      * Focus the button
12304      */
12305     focus : function(){
12306         if(this.el){
12307             this.el.child("button:first").focus();
12308         }
12309     },
12310
12311     // private
12312     onClick : function(e){
12313         e.preventDefault();
12314         if(!this.disabled){
12315             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12316                 if(this.menu && !this.menu.isVisible()){
12317                     this.menu.show(this.el, this.menuAlign);
12318                 }
12319                 this.fireEvent("arrowclick", this, e);
12320                 if(this.arrowHandler){
12321                     this.arrowHandler.call(this.scope || this, this, e);
12322                 }
12323             }else{
12324                 this.fireEvent("click", this, e);
12325                 if(this.handler){
12326                     this.handler.call(this.scope || this, this, e);
12327                 }
12328             }
12329         }
12330     },
12331     // private
12332     onMouseDown : function(e){
12333         if(!this.disabled){
12334             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12335         }
12336     },
12337     // private
12338     onMouseUp : function(e){
12339         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12340     }   
12341 });
12342
12343
12344 // backwards compat
12345 Roo.MenuButton = Roo.SplitButton;/*
12346  * Based on:
12347  * Ext JS Library 1.1.1
12348  * Copyright(c) 2006-2007, Ext JS, LLC.
12349  *
12350  * Originally Released Under LGPL - original licence link has changed is not relivant.
12351  *
12352  * Fork - LGPL
12353  * <script type="text/javascript">
12354  */
12355
12356 /**
12357  * @class Roo.Toolbar
12358  * Basic Toolbar class.
12359  * @constructor
12360  * Creates a new Toolbar
12361  * @param {Object} container The config object
12362  */ 
12363 Roo.Toolbar = function(container, buttons, config)
12364 {
12365     /// old consturctor format still supported..
12366     if(container instanceof Array){ // omit the container for later rendering
12367         buttons = container;
12368         config = buttons;
12369         container = null;
12370     }
12371     if (typeof(container) == 'object' && container.xtype) {
12372         config = container;
12373         container = config.container;
12374         buttons = config.buttons || []; // not really - use items!!
12375     }
12376     var xitems = [];
12377     if (config && config.items) {
12378         xitems = config.items;
12379         delete config.items;
12380     }
12381     Roo.apply(this, config);
12382     this.buttons = buttons;
12383     
12384     if(container){
12385         this.render(container);
12386     }
12387     this.xitems = xitems;
12388     Roo.each(xitems, function(b) {
12389         this.add(b);
12390     }, this);
12391     
12392 };
12393
12394 Roo.Toolbar.prototype = {
12395     /**
12396      * @cfg {Array} items
12397      * array of button configs or elements to add (will be converted to a MixedCollection)
12398      */
12399     
12400     /**
12401      * @cfg {String/HTMLElement/Element} container
12402      * The id or element that will contain the toolbar
12403      */
12404     // private
12405     render : function(ct){
12406         this.el = Roo.get(ct);
12407         if(this.cls){
12408             this.el.addClass(this.cls);
12409         }
12410         // using a table allows for vertical alignment
12411         // 100% width is needed by Safari...
12412         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12413         this.tr = this.el.child("tr", true);
12414         var autoId = 0;
12415         this.items = new Roo.util.MixedCollection(false, function(o){
12416             return o.id || ("item" + (++autoId));
12417         });
12418         if(this.buttons){
12419             this.add.apply(this, this.buttons);
12420             delete this.buttons;
12421         }
12422     },
12423
12424     /**
12425      * Adds element(s) to the toolbar -- this function takes a variable number of 
12426      * arguments of mixed type and adds them to the toolbar.
12427      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12428      * <ul>
12429      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12430      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12431      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12432      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12433      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12434      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12435      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12436      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12437      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12438      * </ul>
12439      * @param {Mixed} arg2
12440      * @param {Mixed} etc.
12441      */
12442     add : function(){
12443         var a = arguments, l = a.length;
12444         for(var i = 0; i < l; i++){
12445             this._add(a[i]);
12446         }
12447     },
12448     // private..
12449     _add : function(el) {
12450         
12451         if (el.xtype) {
12452             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12453         }
12454         
12455         if (el.applyTo){ // some kind of form field
12456             return this.addField(el);
12457         } 
12458         if (el.render){ // some kind of Toolbar.Item
12459             return this.addItem(el);
12460         }
12461         if (typeof el == "string"){ // string
12462             if(el == "separator" || el == "-"){
12463                 return this.addSeparator();
12464             }
12465             if (el == " "){
12466                 return this.addSpacer();
12467             }
12468             if(el == "->"){
12469                 return this.addFill();
12470             }
12471             return this.addText(el);
12472             
12473         }
12474         if(el.tagName){ // element
12475             return this.addElement(el);
12476         }
12477         if(typeof el == "object"){ // must be button config?
12478             return this.addButton(el);
12479         }
12480         // and now what?!?!
12481         return false;
12482         
12483     },
12484     
12485     /**
12486      * Add an Xtype element
12487      * @param {Object} xtype Xtype Object
12488      * @return {Object} created Object
12489      */
12490     addxtype : function(e){
12491         return this.add(e);  
12492     },
12493     
12494     /**
12495      * Returns the Element for this toolbar.
12496      * @return {Roo.Element}
12497      */
12498     getEl : function(){
12499         return this.el;  
12500     },
12501     
12502     /**
12503      * Adds a separator
12504      * @return {Roo.Toolbar.Item} The separator item
12505      */
12506     addSeparator : function(){
12507         return this.addItem(new Roo.Toolbar.Separator());
12508     },
12509
12510     /**
12511      * Adds a spacer element
12512      * @return {Roo.Toolbar.Spacer} The spacer item
12513      */
12514     addSpacer : function(){
12515         return this.addItem(new Roo.Toolbar.Spacer());
12516     },
12517
12518     /**
12519      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12520      * @return {Roo.Toolbar.Fill} The fill item
12521      */
12522     addFill : function(){
12523         return this.addItem(new Roo.Toolbar.Fill());
12524     },
12525
12526     /**
12527      * Adds any standard HTML element to the toolbar
12528      * @param {String/HTMLElement/Element} el The element or id of the element to add
12529      * @return {Roo.Toolbar.Item} The element's item
12530      */
12531     addElement : function(el){
12532         return this.addItem(new Roo.Toolbar.Item(el));
12533     },
12534     /**
12535      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12536      * @type Roo.util.MixedCollection  
12537      */
12538     items : false,
12539      
12540     /**
12541      * Adds any Toolbar.Item or subclass
12542      * @param {Roo.Toolbar.Item} item
12543      * @return {Roo.Toolbar.Item} The item
12544      */
12545     addItem : function(item){
12546         var td = this.nextBlock();
12547         item.render(td);
12548         this.items.add(item);
12549         return item;
12550     },
12551     
12552     /**
12553      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12554      * @param {Object/Array} config A button config or array of configs
12555      * @return {Roo.Toolbar.Button/Array}
12556      */
12557     addButton : function(config){
12558         if(config instanceof Array){
12559             var buttons = [];
12560             for(var i = 0, len = config.length; i < len; i++) {
12561                 buttons.push(this.addButton(config[i]));
12562             }
12563             return buttons;
12564         }
12565         var b = config;
12566         if(!(config instanceof Roo.Toolbar.Button)){
12567             b = config.split ?
12568                 new Roo.Toolbar.SplitButton(config) :
12569                 new Roo.Toolbar.Button(config);
12570         }
12571         var td = this.nextBlock();
12572         b.render(td);
12573         this.items.add(b);
12574         return b;
12575     },
12576     
12577     /**
12578      * Adds text to the toolbar
12579      * @param {String} text The text to add
12580      * @return {Roo.Toolbar.Item} The element's item
12581      */
12582     addText : function(text){
12583         return this.addItem(new Roo.Toolbar.TextItem(text));
12584     },
12585     
12586     /**
12587      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12588      * @param {Number} index The index where the item is to be inserted
12589      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12590      * @return {Roo.Toolbar.Button/Item}
12591      */
12592     insertButton : function(index, item){
12593         if(item instanceof Array){
12594             var buttons = [];
12595             for(var i = 0, len = item.length; i < len; i++) {
12596                buttons.push(this.insertButton(index + i, item[i]));
12597             }
12598             return buttons;
12599         }
12600         if (!(item instanceof Roo.Toolbar.Button)){
12601            item = new Roo.Toolbar.Button(item);
12602         }
12603         var td = document.createElement("td");
12604         this.tr.insertBefore(td, this.tr.childNodes[index]);
12605         item.render(td);
12606         this.items.insert(index, item);
12607         return item;
12608     },
12609     
12610     /**
12611      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12612      * @param {Object} config
12613      * @return {Roo.Toolbar.Item} The element's item
12614      */
12615     addDom : function(config, returnEl){
12616         var td = this.nextBlock();
12617         Roo.DomHelper.overwrite(td, config);
12618         var ti = new Roo.Toolbar.Item(td.firstChild);
12619         ti.render(td);
12620         this.items.add(ti);
12621         return ti;
12622     },
12623
12624     /**
12625      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12626      * @type Roo.util.MixedCollection  
12627      */
12628     fields : false,
12629     
12630     /**
12631      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12632      * Note: the field should not have been rendered yet. For a field that has already been
12633      * rendered, use {@link #addElement}.
12634      * @param {Roo.form.Field} field
12635      * @return {Roo.ToolbarItem}
12636      */
12637      
12638       
12639     addField : function(field) {
12640         if (!this.fields) {
12641             var autoId = 0;
12642             this.fields = new Roo.util.MixedCollection(false, function(o){
12643                 return o.id || ("item" + (++autoId));
12644             });
12645
12646         }
12647         
12648         var td = this.nextBlock();
12649         field.render(td);
12650         var ti = new Roo.Toolbar.Item(td.firstChild);
12651         ti.render(td);
12652         this.items.add(ti);
12653         this.fields.add(field);
12654         return ti;
12655     },
12656     /**
12657      * Hide the toolbar
12658      * @method hide
12659      */
12660      
12661       
12662     hide : function()
12663     {
12664         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12665         this.el.child('div').hide();
12666     },
12667     /**
12668      * Show the toolbar
12669      * @method show
12670      */
12671     show : function()
12672     {
12673         this.el.child('div').show();
12674     },
12675       
12676     // private
12677     nextBlock : function(){
12678         var td = document.createElement("td");
12679         this.tr.appendChild(td);
12680         return td;
12681     },
12682
12683     // private
12684     destroy : function(){
12685         if(this.items){ // rendered?
12686             Roo.destroy.apply(Roo, this.items.items);
12687         }
12688         if(this.fields){ // rendered?
12689             Roo.destroy.apply(Roo, this.fields.items);
12690         }
12691         Roo.Element.uncache(this.el, this.tr);
12692     }
12693 };
12694
12695 /**
12696  * @class Roo.Toolbar.Item
12697  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12698  * @constructor
12699  * Creates a new Item
12700  * @param {HTMLElement} el 
12701  */
12702 Roo.Toolbar.Item = function(el){
12703     this.el = Roo.getDom(el);
12704     this.id = Roo.id(this.el);
12705     this.hidden = false;
12706 };
12707
12708 Roo.Toolbar.Item.prototype = {
12709     
12710     /**
12711      * Get this item's HTML Element
12712      * @return {HTMLElement}
12713      */
12714     getEl : function(){
12715        return this.el;  
12716     },
12717
12718     // private
12719     render : function(td){
12720         this.td = td;
12721         td.appendChild(this.el);
12722     },
12723     
12724     /**
12725      * Removes and destroys this item.
12726      */
12727     destroy : function(){
12728         this.td.parentNode.removeChild(this.td);
12729     },
12730     
12731     /**
12732      * Shows this item.
12733      */
12734     show: function(){
12735         this.hidden = false;
12736         this.td.style.display = "";
12737     },
12738     
12739     /**
12740      * Hides this item.
12741      */
12742     hide: function(){
12743         this.hidden = true;
12744         this.td.style.display = "none";
12745     },
12746     
12747     /**
12748      * Convenience function for boolean show/hide.
12749      * @param {Boolean} visible true to show/false to hide
12750      */
12751     setVisible: function(visible){
12752         if(visible) {
12753             this.show();
12754         }else{
12755             this.hide();
12756         }
12757     },
12758     
12759     /**
12760      * Try to focus this item.
12761      */
12762     focus : function(){
12763         Roo.fly(this.el).focus();
12764     },
12765     
12766     /**
12767      * Disables this item.
12768      */
12769     disable : function(){
12770         Roo.fly(this.td).addClass("x-item-disabled");
12771         this.disabled = true;
12772         this.el.disabled = true;
12773     },
12774     
12775     /**
12776      * Enables this item.
12777      */
12778     enable : function(){
12779         Roo.fly(this.td).removeClass("x-item-disabled");
12780         this.disabled = false;
12781         this.el.disabled = false;
12782     }
12783 };
12784
12785
12786 /**
12787  * @class Roo.Toolbar.Separator
12788  * @extends Roo.Toolbar.Item
12789  * A simple toolbar separator class
12790  * @constructor
12791  * Creates a new Separator
12792  */
12793 Roo.Toolbar.Separator = function(){
12794     var s = document.createElement("span");
12795     s.className = "ytb-sep";
12796     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12797 };
12798 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12799     enable:Roo.emptyFn,
12800     disable:Roo.emptyFn,
12801     focus:Roo.emptyFn
12802 });
12803
12804 /**
12805  * @class Roo.Toolbar.Spacer
12806  * @extends Roo.Toolbar.Item
12807  * A simple element that adds extra horizontal space to a toolbar.
12808  * @constructor
12809  * Creates a new Spacer
12810  */
12811 Roo.Toolbar.Spacer = function(){
12812     var s = document.createElement("div");
12813     s.className = "ytb-spacer";
12814     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12815 };
12816 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12817     enable:Roo.emptyFn,
12818     disable:Roo.emptyFn,
12819     focus:Roo.emptyFn
12820 });
12821
12822 /**
12823  * @class Roo.Toolbar.Fill
12824  * @extends Roo.Toolbar.Spacer
12825  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12826  * @constructor
12827  * Creates a new Spacer
12828  */
12829 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12830     // private
12831     render : function(td){
12832         td.style.width = '100%';
12833         Roo.Toolbar.Fill.superclass.render.call(this, td);
12834     }
12835 });
12836
12837 /**
12838  * @class Roo.Toolbar.TextItem
12839  * @extends Roo.Toolbar.Item
12840  * A simple class that renders text directly into a toolbar.
12841  * @constructor
12842  * Creates a new TextItem
12843  * @param {String} text
12844  */
12845 Roo.Toolbar.TextItem = function(text){
12846     if (typeof(text) == 'object') {
12847         text = text.text;
12848     }
12849     var s = document.createElement("span");
12850     s.className = "ytb-text";
12851     s.innerHTML = text;
12852     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12853 };
12854 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12855     enable:Roo.emptyFn,
12856     disable:Roo.emptyFn,
12857     focus:Roo.emptyFn
12858 });
12859
12860 /**
12861  * @class Roo.Toolbar.Button
12862  * @extends Roo.Button
12863  * A button that renders into a toolbar.
12864  * @constructor
12865  * Creates a new Button
12866  * @param {Object} config A standard {@link Roo.Button} config object
12867  */
12868 Roo.Toolbar.Button = function(config){
12869     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12870 };
12871 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12872     render : function(td){
12873         this.td = td;
12874         Roo.Toolbar.Button.superclass.render.call(this, td);
12875     },
12876     
12877     /**
12878      * Removes and destroys this button
12879      */
12880     destroy : function(){
12881         Roo.Toolbar.Button.superclass.destroy.call(this);
12882         this.td.parentNode.removeChild(this.td);
12883     },
12884     
12885     /**
12886      * Shows this button
12887      */
12888     show: function(){
12889         this.hidden = false;
12890         this.td.style.display = "";
12891     },
12892     
12893     /**
12894      * Hides this button
12895      */
12896     hide: function(){
12897         this.hidden = true;
12898         this.td.style.display = "none";
12899     },
12900
12901     /**
12902      * Disables this item
12903      */
12904     disable : function(){
12905         Roo.fly(this.td).addClass("x-item-disabled");
12906         this.disabled = true;
12907     },
12908
12909     /**
12910      * Enables this item
12911      */
12912     enable : function(){
12913         Roo.fly(this.td).removeClass("x-item-disabled");
12914         this.disabled = false;
12915     }
12916 });
12917 // backwards compat
12918 Roo.ToolbarButton = Roo.Toolbar.Button;
12919
12920 /**
12921  * @class Roo.Toolbar.SplitButton
12922  * @extends Roo.SplitButton
12923  * A menu button that renders into a toolbar.
12924  * @constructor
12925  * Creates a new SplitButton
12926  * @param {Object} config A standard {@link Roo.SplitButton} config object
12927  */
12928 Roo.Toolbar.SplitButton = function(config){
12929     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12930 };
12931 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12932     render : function(td){
12933         this.td = td;
12934         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12935     },
12936     
12937     /**
12938      * Removes and destroys this button
12939      */
12940     destroy : function(){
12941         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12942         this.td.parentNode.removeChild(this.td);
12943     },
12944     
12945     /**
12946      * Shows this button
12947      */
12948     show: function(){
12949         this.hidden = false;
12950         this.td.style.display = "";
12951     },
12952     
12953     /**
12954      * Hides this button
12955      */
12956     hide: function(){
12957         this.hidden = true;
12958         this.td.style.display = "none";
12959     }
12960 });
12961
12962 // backwards compat
12963 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12964  * Based on:
12965  * Ext JS Library 1.1.1
12966  * Copyright(c) 2006-2007, Ext JS, LLC.
12967  *
12968  * Originally Released Under LGPL - original licence link has changed is not relivant.
12969  *
12970  * Fork - LGPL
12971  * <script type="text/javascript">
12972  */
12973  
12974 /**
12975  * @class Roo.PagingToolbar
12976  * @extends Roo.Toolbar
12977  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12978  * @constructor
12979  * Create a new PagingToolbar
12980  * @param {Object} config The config object
12981  */
12982 Roo.PagingToolbar = function(el, ds, config)
12983 {
12984     // old args format still supported... - xtype is prefered..
12985     if (typeof(el) == 'object' && el.xtype) {
12986         // created from xtype...
12987         config = el;
12988         ds = el.dataSource;
12989         el = config.container;
12990     }
12991     var items = [];
12992     if (config.items) {
12993         items = config.items;
12994         config.items = [];
12995     }
12996     
12997     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12998     this.ds = ds;
12999     this.cursor = 0;
13000     this.renderButtons(this.el);
13001     this.bind(ds);
13002     
13003     // supprot items array.
13004    
13005     Roo.each(items, function(e) {
13006         this.add(Roo.factory(e));
13007     },this);
13008     
13009 };
13010
13011 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13012     /**
13013      * @cfg {Roo.data.Store} dataSource
13014      * The underlying data store providing the paged data
13015      */
13016     /**
13017      * @cfg {String/HTMLElement/Element} container
13018      * container The id or element that will contain the toolbar
13019      */
13020     /**
13021      * @cfg {Boolean} displayInfo
13022      * True to display the displayMsg (defaults to false)
13023      */
13024     /**
13025      * @cfg {Number} pageSize
13026      * The number of records to display per page (defaults to 20)
13027      */
13028     pageSize: 20,
13029     /**
13030      * @cfg {String} displayMsg
13031      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13032      */
13033     displayMsg : 'Displaying {0} - {1} of {2}',
13034     /**
13035      * @cfg {String} emptyMsg
13036      * The message to display when no records are found (defaults to "No data to display")
13037      */
13038     emptyMsg : 'No data to display',
13039     /**
13040      * Customizable piece of the default paging text (defaults to "Page")
13041      * @type String
13042      */
13043     beforePageText : "Page",
13044     /**
13045      * Customizable piece of the default paging text (defaults to "of %0")
13046      * @type String
13047      */
13048     afterPageText : "of {0}",
13049     /**
13050      * Customizable piece of the default paging text (defaults to "First Page")
13051      * @type String
13052      */
13053     firstText : "First Page",
13054     /**
13055      * Customizable piece of the default paging text (defaults to "Previous Page")
13056      * @type String
13057      */
13058     prevText : "Previous Page",
13059     /**
13060      * Customizable piece of the default paging text (defaults to "Next Page")
13061      * @type String
13062      */
13063     nextText : "Next Page",
13064     /**
13065      * Customizable piece of the default paging text (defaults to "Last Page")
13066      * @type String
13067      */
13068     lastText : "Last Page",
13069     /**
13070      * Customizable piece of the default paging text (defaults to "Refresh")
13071      * @type String
13072      */
13073     refreshText : "Refresh",
13074
13075     // private
13076     renderButtons : function(el){
13077         Roo.PagingToolbar.superclass.render.call(this, el);
13078         this.first = this.addButton({
13079             tooltip: this.firstText,
13080             cls: "x-btn-icon x-grid-page-first",
13081             disabled: true,
13082             handler: this.onClick.createDelegate(this, ["first"])
13083         });
13084         this.prev = this.addButton({
13085             tooltip: this.prevText,
13086             cls: "x-btn-icon x-grid-page-prev",
13087             disabled: true,
13088             handler: this.onClick.createDelegate(this, ["prev"])
13089         });
13090         //this.addSeparator();
13091         this.add(this.beforePageText);
13092         this.field = Roo.get(this.addDom({
13093            tag: "input",
13094            type: "text",
13095            size: "3",
13096            value: "1",
13097            cls: "x-grid-page-number"
13098         }).el);
13099         this.field.on("keydown", this.onPagingKeydown, this);
13100         this.field.on("focus", function(){this.dom.select();});
13101         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13102         this.field.setHeight(18);
13103         //this.addSeparator();
13104         this.next = this.addButton({
13105             tooltip: this.nextText,
13106             cls: "x-btn-icon x-grid-page-next",
13107             disabled: true,
13108             handler: this.onClick.createDelegate(this, ["next"])
13109         });
13110         this.last = this.addButton({
13111             tooltip: this.lastText,
13112             cls: "x-btn-icon x-grid-page-last",
13113             disabled: true,
13114             handler: this.onClick.createDelegate(this, ["last"])
13115         });
13116         //this.addSeparator();
13117         this.loading = this.addButton({
13118             tooltip: this.refreshText,
13119             cls: "x-btn-icon x-grid-loading",
13120             handler: this.onClick.createDelegate(this, ["refresh"])
13121         });
13122
13123         if(this.displayInfo){
13124             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13125         }
13126     },
13127
13128     // private
13129     updateInfo : function(){
13130         if(this.displayEl){
13131             var count = this.ds.getCount();
13132             var msg = count == 0 ?
13133                 this.emptyMsg :
13134                 String.format(
13135                     this.displayMsg,
13136                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13137                 );
13138             this.displayEl.update(msg);
13139         }
13140     },
13141
13142     // private
13143     onLoad : function(ds, r, o){
13144        this.cursor = o.params ? o.params.start : 0;
13145        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13146
13147        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13148        this.field.dom.value = ap;
13149        this.first.setDisabled(ap == 1);
13150        this.prev.setDisabled(ap == 1);
13151        this.next.setDisabled(ap == ps);
13152        this.last.setDisabled(ap == ps);
13153        this.loading.enable();
13154        this.updateInfo();
13155     },
13156
13157     // private
13158     getPageData : function(){
13159         var total = this.ds.getTotalCount();
13160         return {
13161             total : total,
13162             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13163             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13164         };
13165     },
13166
13167     // private
13168     onLoadError : function(){
13169         this.loading.enable();
13170     },
13171
13172     // private
13173     onPagingKeydown : function(e){
13174         var k = e.getKey();
13175         var d = this.getPageData();
13176         if(k == e.RETURN){
13177             var v = this.field.dom.value, pageNum;
13178             if(!v || isNaN(pageNum = parseInt(v, 10))){
13179                 this.field.dom.value = d.activePage;
13180                 return;
13181             }
13182             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13183             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13184             e.stopEvent();
13185         }
13186         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))
13187         {
13188           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13189           this.field.dom.value = pageNum;
13190           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13191           e.stopEvent();
13192         }
13193         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13194         {
13195           var v = this.field.dom.value, pageNum; 
13196           var increment = (e.shiftKey) ? 10 : 1;
13197           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13198             increment *= -1;
13199           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13200             this.field.dom.value = d.activePage;
13201             return;
13202           }
13203           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13204           {
13205             this.field.dom.value = parseInt(v, 10) + increment;
13206             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13207             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13208           }
13209           e.stopEvent();
13210         }
13211     },
13212
13213     // private
13214     beforeLoad : function(){
13215         if(this.loading){
13216             this.loading.disable();
13217         }
13218     },
13219
13220     // private
13221     onClick : function(which){
13222         var ds = this.ds;
13223         switch(which){
13224             case "first":
13225                 ds.load({params:{start: 0, limit: this.pageSize}});
13226             break;
13227             case "prev":
13228                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13229             break;
13230             case "next":
13231                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13232             break;
13233             case "last":
13234                 var total = ds.getTotalCount();
13235                 var extra = total % this.pageSize;
13236                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13237                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13238             break;
13239             case "refresh":
13240                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13241             break;
13242         }
13243     },
13244
13245     /**
13246      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13247      * @param {Roo.data.Store} store The data store to unbind
13248      */
13249     unbind : function(ds){
13250         ds.un("beforeload", this.beforeLoad, this);
13251         ds.un("load", this.onLoad, this);
13252         ds.un("loadexception", this.onLoadError, this);
13253         ds.un("remove", this.updateInfo, this);
13254         ds.un("add", this.updateInfo, this);
13255         this.ds = undefined;
13256     },
13257
13258     /**
13259      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13260      * @param {Roo.data.Store} store The data store to bind
13261      */
13262     bind : function(ds){
13263         ds.on("beforeload", this.beforeLoad, this);
13264         ds.on("load", this.onLoad, this);
13265         ds.on("loadexception", this.onLoadError, this);
13266         ds.on("remove", this.updateInfo, this);
13267         ds.on("add", this.updateInfo, this);
13268         this.ds = ds;
13269     }
13270 });/*
13271  * Based on:
13272  * Ext JS Library 1.1.1
13273  * Copyright(c) 2006-2007, Ext JS, LLC.
13274  *
13275  * Originally Released Under LGPL - original licence link has changed is not relivant.
13276  *
13277  * Fork - LGPL
13278  * <script type="text/javascript">
13279  */
13280
13281 /**
13282  * @class Roo.Resizable
13283  * @extends Roo.util.Observable
13284  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13285  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13286  * 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
13287  * the element will be wrapped for you automatically.</p>
13288  * <p>Here is the list of valid resize handles:</p>
13289  * <pre>
13290 Value   Description
13291 ------  -------------------
13292  'n'     north
13293  's'     south
13294  'e'     east
13295  'w'     west
13296  'nw'    northwest
13297  'sw'    southwest
13298  'se'    southeast
13299  'ne'    northeast
13300  'hd'    horizontal drag
13301  'all'   all
13302 </pre>
13303  * <p>Here's an example showing the creation of a typical Resizable:</p>
13304  * <pre><code>
13305 var resizer = new Roo.Resizable("element-id", {
13306     handles: 'all',
13307     minWidth: 200,
13308     minHeight: 100,
13309     maxWidth: 500,
13310     maxHeight: 400,
13311     pinned: true
13312 });
13313 resizer.on("resize", myHandler);
13314 </code></pre>
13315  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13316  * resizer.east.setDisplayed(false);</p>
13317  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13318  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13319  * resize operation's new size (defaults to [0, 0])
13320  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13321  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13322  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13323  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13324  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13325  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13326  * @cfg {Number} width The width of the element in pixels (defaults to null)
13327  * @cfg {Number} height The height of the element in pixels (defaults to null)
13328  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13329  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13330  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13331  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13332  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13333  * in favor of the handles config option (defaults to false)
13334  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13335  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13336  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13337  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13338  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13339  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13340  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13341  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13342  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13343  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13344  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13345  * @constructor
13346  * Create a new resizable component
13347  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13348  * @param {Object} config configuration options
13349   */
13350 Roo.Resizable = function(el, config)
13351 {
13352     this.el = Roo.get(el);
13353
13354     if(config && config.wrap){
13355         config.resizeChild = this.el;
13356         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13357         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13358         this.el.setStyle("overflow", "hidden");
13359         this.el.setPositioning(config.resizeChild.getPositioning());
13360         config.resizeChild.clearPositioning();
13361         if(!config.width || !config.height){
13362             var csize = config.resizeChild.getSize();
13363             this.el.setSize(csize.width, csize.height);
13364         }
13365         if(config.pinned && !config.adjustments){
13366             config.adjustments = "auto";
13367         }
13368     }
13369
13370     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13371     this.proxy.unselectable();
13372     this.proxy.enableDisplayMode('block');
13373
13374     Roo.apply(this, config);
13375
13376     if(this.pinned){
13377         this.disableTrackOver = true;
13378         this.el.addClass("x-resizable-pinned");
13379     }
13380     // if the element isn't positioned, make it relative
13381     var position = this.el.getStyle("position");
13382     if(position != "absolute" && position != "fixed"){
13383         this.el.setStyle("position", "relative");
13384     }
13385     if(!this.handles){ // no handles passed, must be legacy style
13386         this.handles = 's,e,se';
13387         if(this.multiDirectional){
13388             this.handles += ',n,w';
13389         }
13390     }
13391     if(this.handles == "all"){
13392         this.handles = "n s e w ne nw se sw";
13393     }
13394     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13395     var ps = Roo.Resizable.positions;
13396     for(var i = 0, len = hs.length; i < len; i++){
13397         if(hs[i] && ps[hs[i]]){
13398             var pos = ps[hs[i]];
13399             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13400         }
13401     }
13402     // legacy
13403     this.corner = this.southeast;
13404     
13405     // updateBox = the box can move..
13406     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13407         this.updateBox = true;
13408     }
13409
13410     this.activeHandle = null;
13411
13412     if(this.resizeChild){
13413         if(typeof this.resizeChild == "boolean"){
13414             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13415         }else{
13416             this.resizeChild = Roo.get(this.resizeChild, true);
13417         }
13418     }
13419     
13420     if(this.adjustments == "auto"){
13421         var rc = this.resizeChild;
13422         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13423         if(rc && (hw || hn)){
13424             rc.position("relative");
13425             rc.setLeft(hw ? hw.el.getWidth() : 0);
13426             rc.setTop(hn ? hn.el.getHeight() : 0);
13427         }
13428         this.adjustments = [
13429             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13430             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13431         ];
13432     }
13433
13434     if(this.draggable){
13435         this.dd = this.dynamic ?
13436             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13437         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13438     }
13439
13440     // public events
13441     this.addEvents({
13442         /**
13443          * @event beforeresize
13444          * Fired before resize is allowed. Set enabled to false to cancel resize.
13445          * @param {Roo.Resizable} this
13446          * @param {Roo.EventObject} e The mousedown event
13447          */
13448         "beforeresize" : true,
13449         /**
13450          * @event resize
13451          * Fired after a resize.
13452          * @param {Roo.Resizable} this
13453          * @param {Number} width The new width
13454          * @param {Number} height The new height
13455          * @param {Roo.EventObject} e The mouseup event
13456          */
13457         "resize" : true
13458     });
13459
13460     if(this.width !== null && this.height !== null){
13461         this.resizeTo(this.width, this.height);
13462     }else{
13463         this.updateChildSize();
13464     }
13465     if(Roo.isIE){
13466         this.el.dom.style.zoom = 1;
13467     }
13468     Roo.Resizable.superclass.constructor.call(this);
13469 };
13470
13471 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13472         resizeChild : false,
13473         adjustments : [0, 0],
13474         minWidth : 5,
13475         minHeight : 5,
13476         maxWidth : 10000,
13477         maxHeight : 10000,
13478         enabled : true,
13479         animate : false,
13480         duration : .35,
13481         dynamic : false,
13482         handles : false,
13483         multiDirectional : false,
13484         disableTrackOver : false,
13485         easing : 'easeOutStrong',
13486         widthIncrement : 0,
13487         heightIncrement : 0,
13488         pinned : false,
13489         width : null,
13490         height : null,
13491         preserveRatio : false,
13492         transparent: false,
13493         minX: 0,
13494         minY: 0,
13495         draggable: false,
13496
13497         /**
13498          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13499          */
13500         constrainTo: undefined,
13501         /**
13502          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13503          */
13504         resizeRegion: undefined,
13505
13506
13507     /**
13508      * Perform a manual resize
13509      * @param {Number} width
13510      * @param {Number} height
13511      */
13512     resizeTo : function(width, height){
13513         this.el.setSize(width, height);
13514         this.updateChildSize();
13515         this.fireEvent("resize", this, width, height, null);
13516     },
13517
13518     // private
13519     startSizing : function(e, handle){
13520         this.fireEvent("beforeresize", this, e);
13521         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13522
13523             if(!this.overlay){
13524                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13525                 this.overlay.unselectable();
13526                 this.overlay.enableDisplayMode("block");
13527                 this.overlay.on("mousemove", this.onMouseMove, this);
13528                 this.overlay.on("mouseup", this.onMouseUp, this);
13529             }
13530             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13531
13532             this.resizing = true;
13533             this.startBox = this.el.getBox();
13534             this.startPoint = e.getXY();
13535             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13536                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13537
13538             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13539             this.overlay.show();
13540
13541             if(this.constrainTo) {
13542                 var ct = Roo.get(this.constrainTo);
13543                 this.resizeRegion = ct.getRegion().adjust(
13544                     ct.getFrameWidth('t'),
13545                     ct.getFrameWidth('l'),
13546                     -ct.getFrameWidth('b'),
13547                     -ct.getFrameWidth('r')
13548                 );
13549             }
13550
13551             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13552             this.proxy.show();
13553             this.proxy.setBox(this.startBox);
13554             if(!this.dynamic){
13555                 this.proxy.setStyle('visibility', 'visible');
13556             }
13557         }
13558     },
13559
13560     // private
13561     onMouseDown : function(handle, e){
13562         if(this.enabled){
13563             e.stopEvent();
13564             this.activeHandle = handle;
13565             this.startSizing(e, handle);
13566         }
13567     },
13568
13569     // private
13570     onMouseUp : function(e){
13571         var size = this.resizeElement();
13572         this.resizing = false;
13573         this.handleOut();
13574         this.overlay.hide();
13575         this.proxy.hide();
13576         this.fireEvent("resize", this, size.width, size.height, e);
13577     },
13578
13579     // private
13580     updateChildSize : function(){
13581         if(this.resizeChild){
13582             var el = this.el;
13583             var child = this.resizeChild;
13584             var adj = this.adjustments;
13585             if(el.dom.offsetWidth){
13586                 var b = el.getSize(true);
13587                 child.setSize(b.width+adj[0], b.height+adj[1]);
13588             }
13589             // Second call here for IE
13590             // The first call enables instant resizing and
13591             // the second call corrects scroll bars if they
13592             // exist
13593             if(Roo.isIE){
13594                 setTimeout(function(){
13595                     if(el.dom.offsetWidth){
13596                         var b = el.getSize(true);
13597                         child.setSize(b.width+adj[0], b.height+adj[1]);
13598                     }
13599                 }, 10);
13600             }
13601         }
13602     },
13603
13604     // private
13605     snap : function(value, inc, min){
13606         if(!inc || !value) return value;
13607         var newValue = value;
13608         var m = value % inc;
13609         if(m > 0){
13610             if(m > (inc/2)){
13611                 newValue = value + (inc-m);
13612             }else{
13613                 newValue = value - m;
13614             }
13615         }
13616         return Math.max(min, newValue);
13617     },
13618
13619     // private
13620     resizeElement : function(){
13621         var box = this.proxy.getBox();
13622         if(this.updateBox){
13623             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13624         }else{
13625             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13626         }
13627         this.updateChildSize();
13628         if(!this.dynamic){
13629             this.proxy.hide();
13630         }
13631         return box;
13632     },
13633
13634     // private
13635     constrain : function(v, diff, m, mx){
13636         if(v - diff < m){
13637             diff = v - m;
13638         }else if(v - diff > mx){
13639             diff = mx - v;
13640         }
13641         return diff;
13642     },
13643
13644     // private
13645     onMouseMove : function(e){
13646         if(this.enabled){
13647             try{// try catch so if something goes wrong the user doesn't get hung
13648
13649             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13650                 return;
13651             }
13652
13653             //var curXY = this.startPoint;
13654             var curSize = this.curSize || this.startBox;
13655             var x = this.startBox.x, y = this.startBox.y;
13656             var ox = x, oy = y;
13657             var w = curSize.width, h = curSize.height;
13658             var ow = w, oh = h;
13659             var mw = this.minWidth, mh = this.minHeight;
13660             var mxw = this.maxWidth, mxh = this.maxHeight;
13661             var wi = this.widthIncrement;
13662             var hi = this.heightIncrement;
13663
13664             var eventXY = e.getXY();
13665             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13666             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13667
13668             var pos = this.activeHandle.position;
13669
13670             switch(pos){
13671                 case "east":
13672                     w += diffX;
13673                     w = Math.min(Math.max(mw, w), mxw);
13674                     break;
13675              
13676                 case "south":
13677                     h += diffY;
13678                     h = Math.min(Math.max(mh, h), mxh);
13679                     break;
13680                 case "southeast":
13681                     w += diffX;
13682                     h += diffY;
13683                     w = Math.min(Math.max(mw, w), mxw);
13684                     h = Math.min(Math.max(mh, h), mxh);
13685                     break;
13686                 case "north":
13687                     diffY = this.constrain(h, diffY, mh, mxh);
13688                     y += diffY;
13689                     h -= diffY;
13690                     break;
13691                 case "hdrag":
13692                     
13693                     if (wi) {
13694                         var adiffX = Math.abs(diffX);
13695                         var sub = (adiffX % wi); // how much 
13696                         if (sub > (wi/2)) { // far enough to snap
13697                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13698                         } else {
13699                             // remove difference.. 
13700                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13701                         }
13702                     }
13703                     x += diffX;
13704                     x = Math.max(this.minX, x);
13705                     break;
13706                 case "west":
13707                     diffX = this.constrain(w, diffX, mw, mxw);
13708                     x += diffX;
13709                     w -= diffX;
13710                     break;
13711                 case "northeast":
13712                     w += diffX;
13713                     w = Math.min(Math.max(mw, w), mxw);
13714                     diffY = this.constrain(h, diffY, mh, mxh);
13715                     y += diffY;
13716                     h -= diffY;
13717                     break;
13718                 case "northwest":
13719                     diffX = this.constrain(w, diffX, mw, mxw);
13720                     diffY = this.constrain(h, diffY, mh, mxh);
13721                     y += diffY;
13722                     h -= diffY;
13723                     x += diffX;
13724                     w -= diffX;
13725                     break;
13726                case "southwest":
13727                     diffX = this.constrain(w, diffX, mw, mxw);
13728                     h += diffY;
13729                     h = Math.min(Math.max(mh, h), mxh);
13730                     x += diffX;
13731                     w -= diffX;
13732                     break;
13733             }
13734
13735             var sw = this.snap(w, wi, mw);
13736             var sh = this.snap(h, hi, mh);
13737             if(sw != w || sh != h){
13738                 switch(pos){
13739                     case "northeast":
13740                         y -= sh - h;
13741                     break;
13742                     case "north":
13743                         y -= sh - h;
13744                         break;
13745                     case "southwest":
13746                         x -= sw - w;
13747                     break;
13748                     case "west":
13749                         x -= sw - w;
13750                         break;
13751                     case "northwest":
13752                         x -= sw - w;
13753                         y -= sh - h;
13754                     break;
13755                 }
13756                 w = sw;
13757                 h = sh;
13758             }
13759
13760             if(this.preserveRatio){
13761                 switch(pos){
13762                     case "southeast":
13763                     case "east":
13764                         h = oh * (w/ow);
13765                         h = Math.min(Math.max(mh, h), mxh);
13766                         w = ow * (h/oh);
13767                        break;
13768                     case "south":
13769                         w = ow * (h/oh);
13770                         w = Math.min(Math.max(mw, w), mxw);
13771                         h = oh * (w/ow);
13772                         break;
13773                     case "northeast":
13774                         w = ow * (h/oh);
13775                         w = Math.min(Math.max(mw, w), mxw);
13776                         h = oh * (w/ow);
13777                     break;
13778                     case "north":
13779                         var tw = w;
13780                         w = ow * (h/oh);
13781                         w = Math.min(Math.max(mw, w), mxw);
13782                         h = oh * (w/ow);
13783                         x += (tw - w) / 2;
13784                         break;
13785                     case "southwest":
13786                         h = oh * (w/ow);
13787                         h = Math.min(Math.max(mh, h), mxh);
13788                         var tw = w;
13789                         w = ow * (h/oh);
13790                         x += tw - w;
13791                         break;
13792                     case "west":
13793                         var th = h;
13794                         h = oh * (w/ow);
13795                         h = Math.min(Math.max(mh, h), mxh);
13796                         y += (th - h) / 2;
13797                         var tw = w;
13798                         w = ow * (h/oh);
13799                         x += tw - w;
13800                        break;
13801                     case "northwest":
13802                         var tw = w;
13803                         var th = h;
13804                         h = oh * (w/ow);
13805                         h = Math.min(Math.max(mh, h), mxh);
13806                         w = ow * (h/oh);
13807                         y += th - h;
13808                         x += tw - w;
13809                        break;
13810
13811                 }
13812             }
13813             if (pos == 'hdrag') {
13814                 w = ow;
13815             }
13816             this.proxy.setBounds(x, y, w, h);
13817             if(this.dynamic){
13818                 this.resizeElement();
13819             }
13820             }catch(e){}
13821         }
13822     },
13823
13824     // private
13825     handleOver : function(){
13826         if(this.enabled){
13827             this.el.addClass("x-resizable-over");
13828         }
13829     },
13830
13831     // private
13832     handleOut : function(){
13833         if(!this.resizing){
13834             this.el.removeClass("x-resizable-over");
13835         }
13836     },
13837
13838     /**
13839      * Returns the element this component is bound to.
13840      * @return {Roo.Element}
13841      */
13842     getEl : function(){
13843         return this.el;
13844     },
13845
13846     /**
13847      * Returns the resizeChild element (or null).
13848      * @return {Roo.Element}
13849      */
13850     getResizeChild : function(){
13851         return this.resizeChild;
13852     },
13853
13854     /**
13855      * Destroys this resizable. If the element was wrapped and
13856      * removeEl is not true then the element remains.
13857      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13858      */
13859     destroy : function(removeEl){
13860         this.proxy.remove();
13861         if(this.overlay){
13862             this.overlay.removeAllListeners();
13863             this.overlay.remove();
13864         }
13865         var ps = Roo.Resizable.positions;
13866         for(var k in ps){
13867             if(typeof ps[k] != "function" && this[ps[k]]){
13868                 var h = this[ps[k]];
13869                 h.el.removeAllListeners();
13870                 h.el.remove();
13871             }
13872         }
13873         if(removeEl){
13874             this.el.update("");
13875             this.el.remove();
13876         }
13877     }
13878 });
13879
13880 // private
13881 // hash to map config positions to true positions
13882 Roo.Resizable.positions = {
13883     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13884     hd: "hdrag"
13885 };
13886
13887 // private
13888 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13889     if(!this.tpl){
13890         // only initialize the template if resizable is used
13891         var tpl = Roo.DomHelper.createTemplate(
13892             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13893         );
13894         tpl.compile();
13895         Roo.Resizable.Handle.prototype.tpl = tpl;
13896     }
13897     this.position = pos;
13898     this.rz = rz;
13899     // show north drag fro topdra
13900     var handlepos = pos == 'hdrag' ? 'north' : pos;
13901     
13902     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13903     if (pos == 'hdrag') {
13904         this.el.setStyle('cursor', 'pointer');
13905     }
13906     this.el.unselectable();
13907     if(transparent){
13908         this.el.setOpacity(0);
13909     }
13910     this.el.on("mousedown", this.onMouseDown, this);
13911     if(!disableTrackOver){
13912         this.el.on("mouseover", this.onMouseOver, this);
13913         this.el.on("mouseout", this.onMouseOut, this);
13914     }
13915 };
13916
13917 // private
13918 Roo.Resizable.Handle.prototype = {
13919     afterResize : function(rz){
13920         // do nothing
13921     },
13922     // private
13923     onMouseDown : function(e){
13924         this.rz.onMouseDown(this, e);
13925     },
13926     // private
13927     onMouseOver : function(e){
13928         this.rz.handleOver(this, e);
13929     },
13930     // private
13931     onMouseOut : function(e){
13932         this.rz.handleOut(this, e);
13933     }
13934 };/*
13935  * Based on:
13936  * Ext JS Library 1.1.1
13937  * Copyright(c) 2006-2007, Ext JS, LLC.
13938  *
13939  * Originally Released Under LGPL - original licence link has changed is not relivant.
13940  *
13941  * Fork - LGPL
13942  * <script type="text/javascript">
13943  */
13944
13945 /**
13946  * @class Roo.Editor
13947  * @extends Roo.Component
13948  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13949  * @constructor
13950  * Create a new Editor
13951  * @param {Roo.form.Field} field The Field object (or descendant)
13952  * @param {Object} config The config object
13953  */
13954 Roo.Editor = function(field, config){
13955     Roo.Editor.superclass.constructor.call(this, config);
13956     this.field = field;
13957     this.addEvents({
13958         /**
13959              * @event beforestartedit
13960              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13961              * false from the handler of this event.
13962              * @param {Editor} this
13963              * @param {Roo.Element} boundEl The underlying element bound to this editor
13964              * @param {Mixed} value The field value being set
13965              */
13966         "beforestartedit" : true,
13967         /**
13968              * @event startedit
13969              * Fires when this editor is displayed
13970              * @param {Roo.Element} boundEl The underlying element bound to this editor
13971              * @param {Mixed} value The starting field value
13972              */
13973         "startedit" : true,
13974         /**
13975              * @event beforecomplete
13976              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13977              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13978              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13979              * event will not fire since no edit actually occurred.
13980              * @param {Editor} this
13981              * @param {Mixed} value The current field value
13982              * @param {Mixed} startValue The original field value
13983              */
13984         "beforecomplete" : true,
13985         /**
13986              * @event complete
13987              * Fires after editing is complete and any changed value has been written to the underlying field.
13988              * @param {Editor} this
13989              * @param {Mixed} value The current field value
13990              * @param {Mixed} startValue The original field value
13991              */
13992         "complete" : true,
13993         /**
13994          * @event specialkey
13995          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13996          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13997          * @param {Roo.form.Field} this
13998          * @param {Roo.EventObject} e The event object
13999          */
14000         "specialkey" : true
14001     });
14002 };
14003
14004 Roo.extend(Roo.Editor, Roo.Component, {
14005     /**
14006      * @cfg {Boolean/String} autosize
14007      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14008      * or "height" to adopt the height only (defaults to false)
14009      */
14010     /**
14011      * @cfg {Boolean} revertInvalid
14012      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14013      * validation fails (defaults to true)
14014      */
14015     /**
14016      * @cfg {Boolean} ignoreNoChange
14017      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14018      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
14019      * will never be ignored.
14020      */
14021     /**
14022      * @cfg {Boolean} hideEl
14023      * False to keep the bound element visible while the editor is displayed (defaults to true)
14024      */
14025     /**
14026      * @cfg {Mixed} value
14027      * The data value of the underlying field (defaults to "")
14028      */
14029     value : "",
14030     /**
14031      * @cfg {String} alignment
14032      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14033      */
14034     alignment: "c-c?",
14035     /**
14036      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14037      * for bottom-right shadow (defaults to "frame")
14038      */
14039     shadow : "frame",
14040     /**
14041      * @cfg {Boolean} constrain True to constrain the editor to the viewport
14042      */
14043     constrain : false,
14044     /**
14045      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14046      */
14047     completeOnEnter : false,
14048     /**
14049      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14050      */
14051     cancelOnEsc : false,
14052     /**
14053      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14054      */
14055     updateEl : false,
14056
14057     // private
14058     onRender : function(ct, position){
14059         this.el = new Roo.Layer({
14060             shadow: this.shadow,
14061             cls: "x-editor",
14062             parentEl : ct,
14063             shim : this.shim,
14064             shadowOffset:4,
14065             id: this.id,
14066             constrain: this.constrain
14067         });
14068         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14069         if(this.field.msgTarget != 'title'){
14070             this.field.msgTarget = 'qtip';
14071         }
14072         this.field.render(this.el);
14073         if(Roo.isGecko){
14074             this.field.el.dom.setAttribute('autocomplete', 'off');
14075         }
14076         this.field.on("specialkey", this.onSpecialKey, this);
14077         if(this.swallowKeys){
14078             this.field.el.swallowEvent(['keydown','keypress']);
14079         }
14080         this.field.show();
14081         this.field.on("blur", this.onBlur, this);
14082         if(this.field.grow){
14083             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14084         }
14085     },
14086
14087     onSpecialKey : function(field, e)
14088     {
14089         //Roo.log('editor onSpecialKey');
14090         if(this.completeOnEnter && e.getKey() == e.ENTER){
14091             e.stopEvent();
14092             this.completeEdit();
14093             return;
14094         }
14095         // do not fire special key otherwise it might hide close the editor...
14096         if(e.getKey() == e.ENTER){    
14097             return;
14098         }
14099         if(this.cancelOnEsc && e.getKey() == e.ESC){
14100             this.cancelEdit();
14101             return;
14102         } 
14103         this.fireEvent('specialkey', field, e);
14104     
14105     },
14106
14107     /**
14108      * Starts the editing process and shows the editor.
14109      * @param {String/HTMLElement/Element} el The element to edit
14110      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14111       * to the innerHTML of el.
14112      */
14113     startEdit : function(el, value){
14114         if(this.editing){
14115             this.completeEdit();
14116         }
14117         this.boundEl = Roo.get(el);
14118         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14119         if(!this.rendered){
14120             this.render(this.parentEl || document.body);
14121         }
14122         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14123             return;
14124         }
14125         this.startValue = v;
14126         this.field.setValue(v);
14127         if(this.autoSize){
14128             var sz = this.boundEl.getSize();
14129             switch(this.autoSize){
14130                 case "width":
14131                 this.setSize(sz.width,  "");
14132                 break;
14133                 case "height":
14134                 this.setSize("",  sz.height);
14135                 break;
14136                 default:
14137                 this.setSize(sz.width,  sz.height);
14138             }
14139         }
14140         this.el.alignTo(this.boundEl, this.alignment);
14141         this.editing = true;
14142         if(Roo.QuickTips){
14143             Roo.QuickTips.disable();
14144         }
14145         this.show();
14146     },
14147
14148     /**
14149      * Sets the height and width of this editor.
14150      * @param {Number} width The new width
14151      * @param {Number} height The new height
14152      */
14153     setSize : function(w, h){
14154         this.field.setSize(w, h);
14155         if(this.el){
14156             this.el.sync();
14157         }
14158     },
14159
14160     /**
14161      * Realigns the editor to the bound field based on the current alignment config value.
14162      */
14163     realign : function(){
14164         this.el.alignTo(this.boundEl, this.alignment);
14165     },
14166
14167     /**
14168      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14169      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14170      */
14171     completeEdit : function(remainVisible){
14172         if(!this.editing){
14173             return;
14174         }
14175         var v = this.getValue();
14176         if(this.revertInvalid !== false && !this.field.isValid()){
14177             v = this.startValue;
14178             this.cancelEdit(true);
14179         }
14180         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14181             this.editing = false;
14182             this.hide();
14183             return;
14184         }
14185         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14186             this.editing = false;
14187             if(this.updateEl && this.boundEl){
14188                 this.boundEl.update(v);
14189             }
14190             if(remainVisible !== true){
14191                 this.hide();
14192             }
14193             this.fireEvent("complete", this, v, this.startValue);
14194         }
14195     },
14196
14197     // private
14198     onShow : function(){
14199         this.el.show();
14200         if(this.hideEl !== false){
14201             this.boundEl.hide();
14202         }
14203         this.field.show();
14204         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14205             this.fixIEFocus = true;
14206             this.deferredFocus.defer(50, this);
14207         }else{
14208             this.field.focus();
14209         }
14210         this.fireEvent("startedit", this.boundEl, this.startValue);
14211     },
14212
14213     deferredFocus : function(){
14214         if(this.editing){
14215             this.field.focus();
14216         }
14217     },
14218
14219     /**
14220      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14221      * reverted to the original starting value.
14222      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14223      * cancel (defaults to false)
14224      */
14225     cancelEdit : function(remainVisible){
14226         if(this.editing){
14227             this.setValue(this.startValue);
14228             if(remainVisible !== true){
14229                 this.hide();
14230             }
14231         }
14232     },
14233
14234     // private
14235     onBlur : function(){
14236         if(this.allowBlur !== true && this.editing){
14237             this.completeEdit();
14238         }
14239     },
14240
14241     // private
14242     onHide : function(){
14243         if(this.editing){
14244             this.completeEdit();
14245             return;
14246         }
14247         this.field.blur();
14248         if(this.field.collapse){
14249             this.field.collapse();
14250         }
14251         this.el.hide();
14252         if(this.hideEl !== false){
14253             this.boundEl.show();
14254         }
14255         if(Roo.QuickTips){
14256             Roo.QuickTips.enable();
14257         }
14258     },
14259
14260     /**
14261      * Sets the data value of the editor
14262      * @param {Mixed} value Any valid value supported by the underlying field
14263      */
14264     setValue : function(v){
14265         this.field.setValue(v);
14266     },
14267
14268     /**
14269      * Gets the data value of the editor
14270      * @return {Mixed} The data value
14271      */
14272     getValue : function(){
14273         return this.field.getValue();
14274     }
14275 });/*
14276  * Based on:
14277  * Ext JS Library 1.1.1
14278  * Copyright(c) 2006-2007, Ext JS, LLC.
14279  *
14280  * Originally Released Under LGPL - original licence link has changed is not relivant.
14281  *
14282  * Fork - LGPL
14283  * <script type="text/javascript">
14284  */
14285  
14286 /**
14287  * @class Roo.BasicDialog
14288  * @extends Roo.util.Observable
14289  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14290  * <pre><code>
14291 var dlg = new Roo.BasicDialog("my-dlg", {
14292     height: 200,
14293     width: 300,
14294     minHeight: 100,
14295     minWidth: 150,
14296     modal: true,
14297     proxyDrag: true,
14298     shadow: true
14299 });
14300 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14301 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14302 dlg.addButton('Cancel', dlg.hide, dlg);
14303 dlg.show();
14304 </code></pre>
14305   <b>A Dialog should always be a direct child of the body element.</b>
14306  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14307  * @cfg {String} title Default text to display in the title bar (defaults to null)
14308  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14309  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14310  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14311  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14312  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14313  * (defaults to null with no animation)
14314  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14315  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14316  * property for valid values (defaults to 'all')
14317  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14318  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14319  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14320  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14321  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14322  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14323  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14324  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14325  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14326  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14327  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14328  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14329  * draggable = true (defaults to false)
14330  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14331  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14332  * shadow (defaults to false)
14333  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14334  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14335  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14336  * @cfg {Array} buttons Array of buttons
14337  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14338  * @constructor
14339  * Create a new BasicDialog.
14340  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14341  * @param {Object} config Configuration options
14342  */
14343 Roo.BasicDialog = function(el, config){
14344     this.el = Roo.get(el);
14345     var dh = Roo.DomHelper;
14346     if(!this.el && config && config.autoCreate){
14347         if(typeof config.autoCreate == "object"){
14348             if(!config.autoCreate.id){
14349                 config.autoCreate.id = el;
14350             }
14351             this.el = dh.append(document.body,
14352                         config.autoCreate, true);
14353         }else{
14354             this.el = dh.append(document.body,
14355                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14356         }
14357     }
14358     el = this.el;
14359     el.setDisplayed(true);
14360     el.hide = this.hideAction;
14361     this.id = el.id;
14362     el.addClass("x-dlg");
14363
14364     Roo.apply(this, config);
14365
14366     this.proxy = el.createProxy("x-dlg-proxy");
14367     this.proxy.hide = this.hideAction;
14368     this.proxy.setOpacity(.5);
14369     this.proxy.hide();
14370
14371     if(config.width){
14372         el.setWidth(config.width);
14373     }
14374     if(config.height){
14375         el.setHeight(config.height);
14376     }
14377     this.size = el.getSize();
14378     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14379         this.xy = [config.x,config.y];
14380     }else{
14381         this.xy = el.getCenterXY(true);
14382     }
14383     /** The header element @type Roo.Element */
14384     this.header = el.child("> .x-dlg-hd");
14385     /** The body element @type Roo.Element */
14386     this.body = el.child("> .x-dlg-bd");
14387     /** The footer element @type Roo.Element */
14388     this.footer = el.child("> .x-dlg-ft");
14389
14390     if(!this.header){
14391         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14392     }
14393     if(!this.body){
14394         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14395     }
14396
14397     this.header.unselectable();
14398     if(this.title){
14399         this.header.update(this.title);
14400     }
14401     // this element allows the dialog to be focused for keyboard event
14402     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14403     this.focusEl.swallowEvent("click", true);
14404
14405     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14406
14407     // wrap the body and footer for special rendering
14408     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14409     if(this.footer){
14410         this.bwrap.dom.appendChild(this.footer.dom);
14411     }
14412
14413     this.bg = this.el.createChild({
14414         tag: "div", cls:"x-dlg-bg",
14415         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14416     });
14417     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14418
14419
14420     if(this.autoScroll !== false && !this.autoTabs){
14421         this.body.setStyle("overflow", "auto");
14422     }
14423
14424     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14425
14426     if(this.closable !== false){
14427         this.el.addClass("x-dlg-closable");
14428         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14429         this.close.on("click", this.closeClick, this);
14430         this.close.addClassOnOver("x-dlg-close-over");
14431     }
14432     if(this.collapsible !== false){
14433         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14434         this.collapseBtn.on("click", this.collapseClick, this);
14435         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14436         this.header.on("dblclick", this.collapseClick, this);
14437     }
14438     if(this.resizable !== false){
14439         this.el.addClass("x-dlg-resizable");
14440         this.resizer = new Roo.Resizable(el, {
14441             minWidth: this.minWidth || 80,
14442             minHeight:this.minHeight || 80,
14443             handles: this.resizeHandles || "all",
14444             pinned: true
14445         });
14446         this.resizer.on("beforeresize", this.beforeResize, this);
14447         this.resizer.on("resize", this.onResize, this);
14448     }
14449     if(this.draggable !== false){
14450         el.addClass("x-dlg-draggable");
14451         if (!this.proxyDrag) {
14452             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14453         }
14454         else {
14455             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14456         }
14457         dd.setHandleElId(this.header.id);
14458         dd.endDrag = this.endMove.createDelegate(this);
14459         dd.startDrag = this.startMove.createDelegate(this);
14460         dd.onDrag = this.onDrag.createDelegate(this);
14461         dd.scroll = false;
14462         this.dd = dd;
14463     }
14464     if(this.modal){
14465         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14466         this.mask.enableDisplayMode("block");
14467         this.mask.hide();
14468         this.el.addClass("x-dlg-modal");
14469     }
14470     if(this.shadow){
14471         this.shadow = new Roo.Shadow({
14472             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14473             offset : this.shadowOffset
14474         });
14475     }else{
14476         this.shadowOffset = 0;
14477     }
14478     if(Roo.useShims && this.shim !== false){
14479         this.shim = this.el.createShim();
14480         this.shim.hide = this.hideAction;
14481         this.shim.hide();
14482     }else{
14483         this.shim = false;
14484     }
14485     if(this.autoTabs){
14486         this.initTabs();
14487     }
14488     if (this.buttons) { 
14489         var bts= this.buttons;
14490         this.buttons = [];
14491         Roo.each(bts, function(b) {
14492             this.addButton(b);
14493         }, this);
14494     }
14495     
14496     
14497     this.addEvents({
14498         /**
14499          * @event keydown
14500          * Fires when a key is pressed
14501          * @param {Roo.BasicDialog} this
14502          * @param {Roo.EventObject} e
14503          */
14504         "keydown" : true,
14505         /**
14506          * @event move
14507          * Fires when this dialog is moved by the user.
14508          * @param {Roo.BasicDialog} this
14509          * @param {Number} x The new page X
14510          * @param {Number} y The new page Y
14511          */
14512         "move" : true,
14513         /**
14514          * @event resize
14515          * Fires when this dialog is resized by the user.
14516          * @param {Roo.BasicDialog} this
14517          * @param {Number} width The new width
14518          * @param {Number} height The new height
14519          */
14520         "resize" : true,
14521         /**
14522          * @event beforehide
14523          * Fires before this dialog is hidden.
14524          * @param {Roo.BasicDialog} this
14525          */
14526         "beforehide" : true,
14527         /**
14528          * @event hide
14529          * Fires when this dialog is hidden.
14530          * @param {Roo.BasicDialog} this
14531          */
14532         "hide" : true,
14533         /**
14534          * @event beforeshow
14535          * Fires before this dialog is shown.
14536          * @param {Roo.BasicDialog} this
14537          */
14538         "beforeshow" : true,
14539         /**
14540          * @event show
14541          * Fires when this dialog is shown.
14542          * @param {Roo.BasicDialog} this
14543          */
14544         "show" : true
14545     });
14546     el.on("keydown", this.onKeyDown, this);
14547     el.on("mousedown", this.toFront, this);
14548     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14549     this.el.hide();
14550     Roo.DialogManager.register(this);
14551     Roo.BasicDialog.superclass.constructor.call(this);
14552 };
14553
14554 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14555     shadowOffset: Roo.isIE ? 6 : 5,
14556     minHeight: 80,
14557     minWidth: 200,
14558     minButtonWidth: 75,
14559     defaultButton: null,
14560     buttonAlign: "right",
14561     tabTag: 'div',
14562     firstShow: true,
14563
14564     /**
14565      * Sets the dialog title text
14566      * @param {String} text The title text to display
14567      * @return {Roo.BasicDialog} this
14568      */
14569     setTitle : function(text){
14570         this.header.update(text);
14571         return this;
14572     },
14573
14574     // private
14575     closeClick : function(){
14576         this.hide();
14577     },
14578
14579     // private
14580     collapseClick : function(){
14581         this[this.collapsed ? "expand" : "collapse"]();
14582     },
14583
14584     /**
14585      * Collapses the dialog to its minimized state (only the title bar is visible).
14586      * Equivalent to the user clicking the collapse dialog button.
14587      */
14588     collapse : function(){
14589         if(!this.collapsed){
14590             this.collapsed = true;
14591             this.el.addClass("x-dlg-collapsed");
14592             this.restoreHeight = this.el.getHeight();
14593             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14594         }
14595     },
14596
14597     /**
14598      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14599      * clicking the expand dialog button.
14600      */
14601     expand : function(){
14602         if(this.collapsed){
14603             this.collapsed = false;
14604             this.el.removeClass("x-dlg-collapsed");
14605             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14606         }
14607     },
14608
14609     /**
14610      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14611      * @return {Roo.TabPanel} The tabs component
14612      */
14613     initTabs : function(){
14614         var tabs = this.getTabs();
14615         while(tabs.getTab(0)){
14616             tabs.removeTab(0);
14617         }
14618         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14619             var dom = el.dom;
14620             tabs.addTab(Roo.id(dom), dom.title);
14621             dom.title = "";
14622         });
14623         tabs.activate(0);
14624         return tabs;
14625     },
14626
14627     // private
14628     beforeResize : function(){
14629         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14630     },
14631
14632     // private
14633     onResize : function(){
14634         this.refreshSize();
14635         this.syncBodyHeight();
14636         this.adjustAssets();
14637         this.focus();
14638         this.fireEvent("resize", this, this.size.width, this.size.height);
14639     },
14640
14641     // private
14642     onKeyDown : function(e){
14643         if(this.isVisible()){
14644             this.fireEvent("keydown", this, e);
14645         }
14646     },
14647
14648     /**
14649      * Resizes the dialog.
14650      * @param {Number} width
14651      * @param {Number} height
14652      * @return {Roo.BasicDialog} this
14653      */
14654     resizeTo : function(width, height){
14655         this.el.setSize(width, height);
14656         this.size = {width: width, height: height};
14657         this.syncBodyHeight();
14658         if(this.fixedcenter){
14659             this.center();
14660         }
14661         if(this.isVisible()){
14662             this.constrainXY();
14663             this.adjustAssets();
14664         }
14665         this.fireEvent("resize", this, width, height);
14666         return this;
14667     },
14668
14669
14670     /**
14671      * Resizes the dialog to fit the specified content size.
14672      * @param {Number} width
14673      * @param {Number} height
14674      * @return {Roo.BasicDialog} this
14675      */
14676     setContentSize : function(w, h){
14677         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14678         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14679         //if(!this.el.isBorderBox()){
14680             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14681             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14682         //}
14683         if(this.tabs){
14684             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14685             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14686         }
14687         this.resizeTo(w, h);
14688         return this;
14689     },
14690
14691     /**
14692      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14693      * executed in response to a particular key being pressed while the dialog is active.
14694      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14695      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14696      * @param {Function} fn The function to call
14697      * @param {Object} scope (optional) The scope of the function
14698      * @return {Roo.BasicDialog} this
14699      */
14700     addKeyListener : function(key, fn, scope){
14701         var keyCode, shift, ctrl, alt;
14702         if(typeof key == "object" && !(key instanceof Array)){
14703             keyCode = key["key"];
14704             shift = key["shift"];
14705             ctrl = key["ctrl"];
14706             alt = key["alt"];
14707         }else{
14708             keyCode = key;
14709         }
14710         var handler = function(dlg, e){
14711             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14712                 var k = e.getKey();
14713                 if(keyCode instanceof Array){
14714                     for(var i = 0, len = keyCode.length; i < len; i++){
14715                         if(keyCode[i] == k){
14716                           fn.call(scope || window, dlg, k, e);
14717                           return;
14718                         }
14719                     }
14720                 }else{
14721                     if(k == keyCode){
14722                         fn.call(scope || window, dlg, k, e);
14723                     }
14724                 }
14725             }
14726         };
14727         this.on("keydown", handler);
14728         return this;
14729     },
14730
14731     /**
14732      * Returns the TabPanel component (creates it if it doesn't exist).
14733      * Note: If you wish to simply check for the existence of tabs without creating them,
14734      * check for a null 'tabs' property.
14735      * @return {Roo.TabPanel} The tabs component
14736      */
14737     getTabs : function(){
14738         if(!this.tabs){
14739             this.el.addClass("x-dlg-auto-tabs");
14740             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14741             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14742         }
14743         return this.tabs;
14744     },
14745
14746     /**
14747      * Adds a button to the footer section of the dialog.
14748      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14749      * object or a valid Roo.DomHelper element config
14750      * @param {Function} handler The function called when the button is clicked
14751      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14752      * @return {Roo.Button} The new button
14753      */
14754     addButton : function(config, handler, scope){
14755         var dh = Roo.DomHelper;
14756         if(!this.footer){
14757             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14758         }
14759         if(!this.btnContainer){
14760             var tb = this.footer.createChild({
14761
14762                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14763                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14764             }, null, true);
14765             this.btnContainer = tb.firstChild.firstChild.firstChild;
14766         }
14767         var bconfig = {
14768             handler: handler,
14769             scope: scope,
14770             minWidth: this.minButtonWidth,
14771             hideParent:true
14772         };
14773         if(typeof config == "string"){
14774             bconfig.text = config;
14775         }else{
14776             if(config.tag){
14777                 bconfig.dhconfig = config;
14778             }else{
14779                 Roo.apply(bconfig, config);
14780             }
14781         }
14782         var fc = false;
14783         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14784             bconfig.position = Math.max(0, bconfig.position);
14785             fc = this.btnContainer.childNodes[bconfig.position];
14786         }
14787          
14788         var btn = new Roo.Button(
14789             fc ? 
14790                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14791                 : this.btnContainer.appendChild(document.createElement("td")),
14792             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14793             bconfig
14794         );
14795         this.syncBodyHeight();
14796         if(!this.buttons){
14797             /**
14798              * Array of all the buttons that have been added to this dialog via addButton
14799              * @type Array
14800              */
14801             this.buttons = [];
14802         }
14803         this.buttons.push(btn);
14804         return btn;
14805     },
14806
14807     /**
14808      * Sets the default button to be focused when the dialog is displayed.
14809      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14810      * @return {Roo.BasicDialog} this
14811      */
14812     setDefaultButton : function(btn){
14813         this.defaultButton = btn;
14814         return this;
14815     },
14816
14817     // private
14818     getHeaderFooterHeight : function(safe){
14819         var height = 0;
14820         if(this.header){
14821            height += this.header.getHeight();
14822         }
14823         if(this.footer){
14824            var fm = this.footer.getMargins();
14825             height += (this.footer.getHeight()+fm.top+fm.bottom);
14826         }
14827         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14828         height += this.centerBg.getPadding("tb");
14829         return height;
14830     },
14831
14832     // private
14833     syncBodyHeight : function(){
14834         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14835         var height = this.size.height - this.getHeaderFooterHeight(false);
14836         bd.setHeight(height-bd.getMargins("tb"));
14837         var hh = this.header.getHeight();
14838         var h = this.size.height-hh;
14839         cb.setHeight(h);
14840         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14841         bw.setHeight(h-cb.getPadding("tb"));
14842         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14843         bd.setWidth(bw.getWidth(true));
14844         if(this.tabs){
14845             this.tabs.syncHeight();
14846             if(Roo.isIE){
14847                 this.tabs.el.repaint();
14848             }
14849         }
14850     },
14851
14852     /**
14853      * Restores the previous state of the dialog if Roo.state is configured.
14854      * @return {Roo.BasicDialog} this
14855      */
14856     restoreState : function(){
14857         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14858         if(box && box.width){
14859             this.xy = [box.x, box.y];
14860             this.resizeTo(box.width, box.height);
14861         }
14862         return this;
14863     },
14864
14865     // private
14866     beforeShow : function(){
14867         this.expand();
14868         if(this.fixedcenter){
14869             this.xy = this.el.getCenterXY(true);
14870         }
14871         if(this.modal){
14872             Roo.get(document.body).addClass("x-body-masked");
14873             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14874             this.mask.show();
14875         }
14876         this.constrainXY();
14877     },
14878
14879     // private
14880     animShow : function(){
14881         var b = Roo.get(this.animateTarget).getBox();
14882         this.proxy.setSize(b.width, b.height);
14883         this.proxy.setLocation(b.x, b.y);
14884         this.proxy.show();
14885         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14886                     true, .35, this.showEl.createDelegate(this));
14887     },
14888
14889     /**
14890      * Shows the dialog.
14891      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14892      * @return {Roo.BasicDialog} this
14893      */
14894     show : function(animateTarget){
14895         if (this.fireEvent("beforeshow", this) === false){
14896             return;
14897         }
14898         if(this.syncHeightBeforeShow){
14899             this.syncBodyHeight();
14900         }else if(this.firstShow){
14901             this.firstShow = false;
14902             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14903         }
14904         this.animateTarget = animateTarget || this.animateTarget;
14905         if(!this.el.isVisible()){
14906             this.beforeShow();
14907             if(this.animateTarget && Roo.get(this.animateTarget)){
14908                 this.animShow();
14909             }else{
14910                 this.showEl();
14911             }
14912         }
14913         return this;
14914     },
14915
14916     // private
14917     showEl : function(){
14918         this.proxy.hide();
14919         this.el.setXY(this.xy);
14920         this.el.show();
14921         this.adjustAssets(true);
14922         this.toFront();
14923         this.focus();
14924         // IE peekaboo bug - fix found by Dave Fenwick
14925         if(Roo.isIE){
14926             this.el.repaint();
14927         }
14928         this.fireEvent("show", this);
14929     },
14930
14931     /**
14932      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14933      * dialog itself will receive focus.
14934      */
14935     focus : function(){
14936         if(this.defaultButton){
14937             this.defaultButton.focus();
14938         }else{
14939             this.focusEl.focus();
14940         }
14941     },
14942
14943     // private
14944     constrainXY : function(){
14945         if(this.constraintoviewport !== false){
14946             if(!this.viewSize){
14947                 if(this.container){
14948                     var s = this.container.getSize();
14949                     this.viewSize = [s.width, s.height];
14950                 }else{
14951                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14952                 }
14953             }
14954             var s = Roo.get(this.container||document).getScroll();
14955
14956             var x = this.xy[0], y = this.xy[1];
14957             var w = this.size.width, h = this.size.height;
14958             var vw = this.viewSize[0], vh = this.viewSize[1];
14959             // only move it if it needs it
14960             var moved = false;
14961             // first validate right/bottom
14962             if(x + w > vw+s.left){
14963                 x = vw - w;
14964                 moved = true;
14965             }
14966             if(y + h > vh+s.top){
14967                 y = vh - h;
14968                 moved = true;
14969             }
14970             // then make sure top/left isn't negative
14971             if(x < s.left){
14972                 x = s.left;
14973                 moved = true;
14974             }
14975             if(y < s.top){
14976                 y = s.top;
14977                 moved = true;
14978             }
14979             if(moved){
14980                 // cache xy
14981                 this.xy = [x, y];
14982                 if(this.isVisible()){
14983                     this.el.setLocation(x, y);
14984                     this.adjustAssets();
14985                 }
14986             }
14987         }
14988     },
14989
14990     // private
14991     onDrag : function(){
14992         if(!this.proxyDrag){
14993             this.xy = this.el.getXY();
14994             this.adjustAssets();
14995         }
14996     },
14997
14998     // private
14999     adjustAssets : function(doShow){
15000         var x = this.xy[0], y = this.xy[1];
15001         var w = this.size.width, h = this.size.height;
15002         if(doShow === true){
15003             if(this.shadow){
15004                 this.shadow.show(this.el);
15005             }
15006             if(this.shim){
15007                 this.shim.show();
15008             }
15009         }
15010         if(this.shadow && this.shadow.isVisible()){
15011             this.shadow.show(this.el);
15012         }
15013         if(this.shim && this.shim.isVisible()){
15014             this.shim.setBounds(x, y, w, h);
15015         }
15016     },
15017
15018     // private
15019     adjustViewport : function(w, h){
15020         if(!w || !h){
15021             w = Roo.lib.Dom.getViewWidth();
15022             h = Roo.lib.Dom.getViewHeight();
15023         }
15024         // cache the size
15025         this.viewSize = [w, h];
15026         if(this.modal && this.mask.isVisible()){
15027             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15028             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15029         }
15030         if(this.isVisible()){
15031             this.constrainXY();
15032         }
15033     },
15034
15035     /**
15036      * Destroys this dialog and all its supporting elements (including any tabs, shim,
15037      * shadow, proxy, mask, etc.)  Also removes all event listeners.
15038      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15039      */
15040     destroy : function(removeEl){
15041         if(this.isVisible()){
15042             this.animateTarget = null;
15043             this.hide();
15044         }
15045         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15046         if(this.tabs){
15047             this.tabs.destroy(removeEl);
15048         }
15049         Roo.destroy(
15050              this.shim,
15051              this.proxy,
15052              this.resizer,
15053              this.close,
15054              this.mask
15055         );
15056         if(this.dd){
15057             this.dd.unreg();
15058         }
15059         if(this.buttons){
15060            for(var i = 0, len = this.buttons.length; i < len; i++){
15061                this.buttons[i].destroy();
15062            }
15063         }
15064         this.el.removeAllListeners();
15065         if(removeEl === true){
15066             this.el.update("");
15067             this.el.remove();
15068         }
15069         Roo.DialogManager.unregister(this);
15070     },
15071
15072     // private
15073     startMove : function(){
15074         if(this.proxyDrag){
15075             this.proxy.show();
15076         }
15077         if(this.constraintoviewport !== false){
15078             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15079         }
15080     },
15081
15082     // private
15083     endMove : function(){
15084         if(!this.proxyDrag){
15085             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15086         }else{
15087             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15088             this.proxy.hide();
15089         }
15090         this.refreshSize();
15091         this.adjustAssets();
15092         this.focus();
15093         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15094     },
15095
15096     /**
15097      * Brings this dialog to the front of any other visible dialogs
15098      * @return {Roo.BasicDialog} this
15099      */
15100     toFront : function(){
15101         Roo.DialogManager.bringToFront(this);
15102         return this;
15103     },
15104
15105     /**
15106      * Sends this dialog to the back (under) of any other visible dialogs
15107      * @return {Roo.BasicDialog} this
15108      */
15109     toBack : function(){
15110         Roo.DialogManager.sendToBack(this);
15111         return this;
15112     },
15113
15114     /**
15115      * Centers this dialog in the viewport
15116      * @return {Roo.BasicDialog} this
15117      */
15118     center : function(){
15119         var xy = this.el.getCenterXY(true);
15120         this.moveTo(xy[0], xy[1]);
15121         return this;
15122     },
15123
15124     /**
15125      * Moves the dialog's top-left corner to the specified point
15126      * @param {Number} x
15127      * @param {Number} y
15128      * @return {Roo.BasicDialog} this
15129      */
15130     moveTo : function(x, y){
15131         this.xy = [x,y];
15132         if(this.isVisible()){
15133             this.el.setXY(this.xy);
15134             this.adjustAssets();
15135         }
15136         return this;
15137     },
15138
15139     /**
15140      * Aligns the dialog to the specified element
15141      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15142      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15143      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15144      * @return {Roo.BasicDialog} this
15145      */
15146     alignTo : function(element, position, offsets){
15147         this.xy = this.el.getAlignToXY(element, position, offsets);
15148         if(this.isVisible()){
15149             this.el.setXY(this.xy);
15150             this.adjustAssets();
15151         }
15152         return this;
15153     },
15154
15155     /**
15156      * Anchors an element to another element and realigns it when the window is resized.
15157      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15158      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15159      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15160      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15161      * is a number, it is used as the buffer delay (defaults to 50ms).
15162      * @return {Roo.BasicDialog} this
15163      */
15164     anchorTo : function(el, alignment, offsets, monitorScroll){
15165         var action = function(){
15166             this.alignTo(el, alignment, offsets);
15167         };
15168         Roo.EventManager.onWindowResize(action, this);
15169         var tm = typeof monitorScroll;
15170         if(tm != 'undefined'){
15171             Roo.EventManager.on(window, 'scroll', action, this,
15172                 {buffer: tm == 'number' ? monitorScroll : 50});
15173         }
15174         action.call(this);
15175         return this;
15176     },
15177
15178     /**
15179      * Returns true if the dialog is visible
15180      * @return {Boolean}
15181      */
15182     isVisible : function(){
15183         return this.el.isVisible();
15184     },
15185
15186     // private
15187     animHide : function(callback){
15188         var b = Roo.get(this.animateTarget).getBox();
15189         this.proxy.show();
15190         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15191         this.el.hide();
15192         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15193                     this.hideEl.createDelegate(this, [callback]));
15194     },
15195
15196     /**
15197      * Hides the dialog.
15198      * @param {Function} callback (optional) Function to call when the dialog is hidden
15199      * @return {Roo.BasicDialog} this
15200      */
15201     hide : function(callback){
15202         if (this.fireEvent("beforehide", this) === false){
15203             return;
15204         }
15205         if(this.shadow){
15206             this.shadow.hide();
15207         }
15208         if(this.shim) {
15209           this.shim.hide();
15210         }
15211         // sometimes animateTarget seems to get set.. causing problems...
15212         // this just double checks..
15213         if(this.animateTarget && Roo.get(this.animateTarget)) {
15214            this.animHide(callback);
15215         }else{
15216             this.el.hide();
15217             this.hideEl(callback);
15218         }
15219         return this;
15220     },
15221
15222     // private
15223     hideEl : function(callback){
15224         this.proxy.hide();
15225         if(this.modal){
15226             this.mask.hide();
15227             Roo.get(document.body).removeClass("x-body-masked");
15228         }
15229         this.fireEvent("hide", this);
15230         if(typeof callback == "function"){
15231             callback();
15232         }
15233     },
15234
15235     // private
15236     hideAction : function(){
15237         this.setLeft("-10000px");
15238         this.setTop("-10000px");
15239         this.setStyle("visibility", "hidden");
15240     },
15241
15242     // private
15243     refreshSize : function(){
15244         this.size = this.el.getSize();
15245         this.xy = this.el.getXY();
15246         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15247     },
15248
15249     // private
15250     // z-index is managed by the DialogManager and may be overwritten at any time
15251     setZIndex : function(index){
15252         if(this.modal){
15253             this.mask.setStyle("z-index", index);
15254         }
15255         if(this.shim){
15256             this.shim.setStyle("z-index", ++index);
15257         }
15258         if(this.shadow){
15259             this.shadow.setZIndex(++index);
15260         }
15261         this.el.setStyle("z-index", ++index);
15262         if(this.proxy){
15263             this.proxy.setStyle("z-index", ++index);
15264         }
15265         if(this.resizer){
15266             this.resizer.proxy.setStyle("z-index", ++index);
15267         }
15268
15269         this.lastZIndex = index;
15270     },
15271
15272     /**
15273      * Returns the element for this dialog
15274      * @return {Roo.Element} The underlying dialog Element
15275      */
15276     getEl : function(){
15277         return this.el;
15278     }
15279 });
15280
15281 /**
15282  * @class Roo.DialogManager
15283  * Provides global access to BasicDialogs that have been created and
15284  * support for z-indexing (layering) multiple open dialogs.
15285  */
15286 Roo.DialogManager = function(){
15287     var list = {};
15288     var accessList = [];
15289     var front = null;
15290
15291     // private
15292     var sortDialogs = function(d1, d2){
15293         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15294     };
15295
15296     // private
15297     var orderDialogs = function(){
15298         accessList.sort(sortDialogs);
15299         var seed = Roo.DialogManager.zseed;
15300         for(var i = 0, len = accessList.length; i < len; i++){
15301             var dlg = accessList[i];
15302             if(dlg){
15303                 dlg.setZIndex(seed + (i*10));
15304             }
15305         }
15306     };
15307
15308     return {
15309         /**
15310          * The starting z-index for BasicDialogs (defaults to 9000)
15311          * @type Number The z-index value
15312          */
15313         zseed : 9000,
15314
15315         // private
15316         register : function(dlg){
15317             list[dlg.id] = dlg;
15318             accessList.push(dlg);
15319         },
15320
15321         // private
15322         unregister : function(dlg){
15323             delete list[dlg.id];
15324             var i=0;
15325             var len=0;
15326             if(!accessList.indexOf){
15327                 for(  i = 0, len = accessList.length; i < len; i++){
15328                     if(accessList[i] == dlg){
15329                         accessList.splice(i, 1);
15330                         return;
15331                     }
15332                 }
15333             }else{
15334                  i = accessList.indexOf(dlg);
15335                 if(i != -1){
15336                     accessList.splice(i, 1);
15337                 }
15338             }
15339         },
15340
15341         /**
15342          * Gets a registered dialog by id
15343          * @param {String/Object} id The id of the dialog or a dialog
15344          * @return {Roo.BasicDialog} this
15345          */
15346         get : function(id){
15347             return typeof id == "object" ? id : list[id];
15348         },
15349
15350         /**
15351          * Brings the specified dialog to the front
15352          * @param {String/Object} dlg The id of the dialog or a dialog
15353          * @return {Roo.BasicDialog} this
15354          */
15355         bringToFront : function(dlg){
15356             dlg = this.get(dlg);
15357             if(dlg != front){
15358                 front = dlg;
15359                 dlg._lastAccess = new Date().getTime();
15360                 orderDialogs();
15361             }
15362             return dlg;
15363         },
15364
15365         /**
15366          * Sends the specified dialog to the back
15367          * @param {String/Object} dlg The id of the dialog or a dialog
15368          * @return {Roo.BasicDialog} this
15369          */
15370         sendToBack : function(dlg){
15371             dlg = this.get(dlg);
15372             dlg._lastAccess = -(new Date().getTime());
15373             orderDialogs();
15374             return dlg;
15375         },
15376
15377         /**
15378          * Hides all dialogs
15379          */
15380         hideAll : function(){
15381             for(var id in list){
15382                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15383                     list[id].hide();
15384                 }
15385             }
15386         }
15387     };
15388 }();
15389
15390 /**
15391  * @class Roo.LayoutDialog
15392  * @extends Roo.BasicDialog
15393  * Dialog which provides adjustments for working with a layout in a Dialog.
15394  * Add your necessary layout config options to the dialog's config.<br>
15395  * Example usage (including a nested layout):
15396  * <pre><code>
15397 if(!dialog){
15398     dialog = new Roo.LayoutDialog("download-dlg", {
15399         modal: true,
15400         width:600,
15401         height:450,
15402         shadow:true,
15403         minWidth:500,
15404         minHeight:350,
15405         autoTabs:true,
15406         proxyDrag:true,
15407         // layout config merges with the dialog config
15408         center:{
15409             tabPosition: "top",
15410             alwaysShowTabs: true
15411         }
15412     });
15413     dialog.addKeyListener(27, dialog.hide, dialog);
15414     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15415     dialog.addButton("Build It!", this.getDownload, this);
15416
15417     // we can even add nested layouts
15418     var innerLayout = new Roo.BorderLayout("dl-inner", {
15419         east: {
15420             initialSize: 200,
15421             autoScroll:true,
15422             split:true
15423         },
15424         center: {
15425             autoScroll:true
15426         }
15427     });
15428     innerLayout.beginUpdate();
15429     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15430     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15431     innerLayout.endUpdate(true);
15432
15433     var layout = dialog.getLayout();
15434     layout.beginUpdate();
15435     layout.add("center", new Roo.ContentPanel("standard-panel",
15436                         {title: "Download the Source", fitToFrame:true}));
15437     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15438                {title: "Build your own roo.js"}));
15439     layout.getRegion("center").showPanel(sp);
15440     layout.endUpdate();
15441 }
15442 </code></pre>
15443     * @constructor
15444     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15445     * @param {Object} config configuration options
15446   */
15447 Roo.LayoutDialog = function(el, cfg){
15448     
15449     var config=  cfg;
15450     if (typeof(cfg) == 'undefined') {
15451         config = Roo.apply({}, el);
15452         // not sure why we use documentElement here.. - it should always be body.
15453         // IE7 borks horribly if we use documentElement.
15454         // webkit also does not like documentElement - it creates a body element...
15455         el = Roo.get( document.body || document.documentElement ).createChild();
15456         //config.autoCreate = true;
15457     }
15458     
15459     
15460     config.autoTabs = false;
15461     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15462     this.body.setStyle({overflow:"hidden", position:"relative"});
15463     this.layout = new Roo.BorderLayout(this.body.dom, config);
15464     this.layout.monitorWindowResize = false;
15465     this.el.addClass("x-dlg-auto-layout");
15466     // fix case when center region overwrites center function
15467     this.center = Roo.BasicDialog.prototype.center;
15468     this.on("show", this.layout.layout, this.layout, true);
15469     if (config.items) {
15470         var xitems = config.items;
15471         delete config.items;
15472         Roo.each(xitems, this.addxtype, this);
15473     }
15474     
15475     
15476 };
15477 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15478     /**
15479      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15480      * @deprecated
15481      */
15482     endUpdate : function(){
15483         this.layout.endUpdate();
15484     },
15485
15486     /**
15487      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15488      *  @deprecated
15489      */
15490     beginUpdate : function(){
15491         this.layout.beginUpdate();
15492     },
15493
15494     /**
15495      * Get the BorderLayout for this dialog
15496      * @return {Roo.BorderLayout}
15497      */
15498     getLayout : function(){
15499         return this.layout;
15500     },
15501
15502     showEl : function(){
15503         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15504         if(Roo.isIE7){
15505             this.layout.layout();
15506         }
15507     },
15508
15509     // private
15510     // Use the syncHeightBeforeShow config option to control this automatically
15511     syncBodyHeight : function(){
15512         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15513         if(this.layout){this.layout.layout();}
15514     },
15515     
15516       /**
15517      * Add an xtype element (actually adds to the layout.)
15518      * @return {Object} xdata xtype object data.
15519      */
15520     
15521     addxtype : function(c) {
15522         return this.layout.addxtype(c);
15523     }
15524 });/*
15525  * Based on:
15526  * Ext JS Library 1.1.1
15527  * Copyright(c) 2006-2007, Ext JS, LLC.
15528  *
15529  * Originally Released Under LGPL - original licence link has changed is not relivant.
15530  *
15531  * Fork - LGPL
15532  * <script type="text/javascript">
15533  */
15534  
15535 /**
15536  * @class Roo.MessageBox
15537  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15538  * Example usage:
15539  *<pre><code>
15540 // Basic alert:
15541 Roo.Msg.alert('Status', 'Changes saved successfully.');
15542
15543 // Prompt for user data:
15544 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15545     if (btn == 'ok'){
15546         // process text value...
15547     }
15548 });
15549
15550 // Show a dialog using config options:
15551 Roo.Msg.show({
15552    title:'Save Changes?',
15553    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15554    buttons: Roo.Msg.YESNOCANCEL,
15555    fn: processResult,
15556    animEl: 'elId'
15557 });
15558 </code></pre>
15559  * @singleton
15560  */
15561 Roo.MessageBox = function(){
15562     var dlg, opt, mask, waitTimer;
15563     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15564     var buttons, activeTextEl, bwidth;
15565
15566     // private
15567     var handleButton = function(button){
15568         dlg.hide();
15569         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15570     };
15571
15572     // private
15573     var handleHide = function(){
15574         if(opt && opt.cls){
15575             dlg.el.removeClass(opt.cls);
15576         }
15577         if(waitTimer){
15578             Roo.TaskMgr.stop(waitTimer);
15579             waitTimer = null;
15580         }
15581     };
15582
15583     // private
15584     var updateButtons = function(b){
15585         var width = 0;
15586         if(!b){
15587             buttons["ok"].hide();
15588             buttons["cancel"].hide();
15589             buttons["yes"].hide();
15590             buttons["no"].hide();
15591             dlg.footer.dom.style.display = 'none';
15592             return width;
15593         }
15594         dlg.footer.dom.style.display = '';
15595         for(var k in buttons){
15596             if(typeof buttons[k] != "function"){
15597                 if(b[k]){
15598                     buttons[k].show();
15599                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15600                     width += buttons[k].el.getWidth()+15;
15601                 }else{
15602                     buttons[k].hide();
15603                 }
15604             }
15605         }
15606         return width;
15607     };
15608
15609     // private
15610     var handleEsc = function(d, k, e){
15611         if(opt && opt.closable !== false){
15612             dlg.hide();
15613         }
15614         if(e){
15615             e.stopEvent();
15616         }
15617     };
15618
15619     return {
15620         /**
15621          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15622          * @return {Roo.BasicDialog} The BasicDialog element
15623          */
15624         getDialog : function(){
15625            if(!dlg){
15626                 dlg = new Roo.BasicDialog("x-msg-box", {
15627                     autoCreate : true,
15628                     shadow: true,
15629                     draggable: true,
15630                     resizable:false,
15631                     constraintoviewport:false,
15632                     fixedcenter:true,
15633                     collapsible : false,
15634                     shim:true,
15635                     modal: true,
15636                     width:400, height:100,
15637                     buttonAlign:"center",
15638                     closeClick : function(){
15639                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15640                             handleButton("no");
15641                         }else{
15642                             handleButton("cancel");
15643                         }
15644                     }
15645                 });
15646                 dlg.on("hide", handleHide);
15647                 mask = dlg.mask;
15648                 dlg.addKeyListener(27, handleEsc);
15649                 buttons = {};
15650                 var bt = this.buttonText;
15651                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15652                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15653                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15654                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15655                 bodyEl = dlg.body.createChild({
15656
15657                     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>'
15658                 });
15659                 msgEl = bodyEl.dom.firstChild;
15660                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15661                 textboxEl.enableDisplayMode();
15662                 textboxEl.addKeyListener([10,13], function(){
15663                     if(dlg.isVisible() && opt && opt.buttons){
15664                         if(opt.buttons.ok){
15665                             handleButton("ok");
15666                         }else if(opt.buttons.yes){
15667                             handleButton("yes");
15668                         }
15669                     }
15670                 });
15671                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15672                 textareaEl.enableDisplayMode();
15673                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15674                 progressEl.enableDisplayMode();
15675                 var pf = progressEl.dom.firstChild;
15676                 if (pf) {
15677                     pp = Roo.get(pf.firstChild);
15678                     pp.setHeight(pf.offsetHeight);
15679                 }
15680                 
15681             }
15682             return dlg;
15683         },
15684
15685         /**
15686          * Updates the message box body text
15687          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15688          * the XHTML-compliant non-breaking space character '&amp;#160;')
15689          * @return {Roo.MessageBox} This message box
15690          */
15691         updateText : function(text){
15692             if(!dlg.isVisible() && !opt.width){
15693                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15694             }
15695             msgEl.innerHTML = text || '&#160;';
15696       
15697             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15698             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15699             var w = Math.max(
15700                     Math.min(opt.width || cw , this.maxWidth), 
15701                     Math.max(opt.minWidth || this.minWidth, bwidth)
15702             );
15703             if(opt.prompt){
15704                 activeTextEl.setWidth(w);
15705             }
15706             if(dlg.isVisible()){
15707                 dlg.fixedcenter = false;
15708             }
15709             // to big, make it scroll. = But as usual stupid IE does not support
15710             // !important..
15711             
15712             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15713                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15714                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15715             } else {
15716                 bodyEl.dom.style.height = '';
15717                 bodyEl.dom.style.overflowY = '';
15718             }
15719             if (cw > w) {
15720                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15721             } else {
15722                 bodyEl.dom.style.overflowX = '';
15723             }
15724             
15725             dlg.setContentSize(w, bodyEl.getHeight());
15726             if(dlg.isVisible()){
15727                 dlg.fixedcenter = true;
15728             }
15729             return this;
15730         },
15731
15732         /**
15733          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15734          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15735          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15736          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15737          * @return {Roo.MessageBox} This message box
15738          */
15739         updateProgress : function(value, text){
15740             if(text){
15741                 this.updateText(text);
15742             }
15743             if (pp) { // weird bug on my firefox - for some reason this is not defined
15744                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15745             }
15746             return this;
15747         },        
15748
15749         /**
15750          * Returns true if the message box is currently displayed
15751          * @return {Boolean} True if the message box is visible, else false
15752          */
15753         isVisible : function(){
15754             return dlg && dlg.isVisible();  
15755         },
15756
15757         /**
15758          * Hides the message box if it is displayed
15759          */
15760         hide : function(){
15761             if(this.isVisible()){
15762                 dlg.hide();
15763             }  
15764         },
15765
15766         /**
15767          * Displays a new message box, or reinitializes an existing message box, based on the config options
15768          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15769          * The following config object properties are supported:
15770          * <pre>
15771 Property    Type             Description
15772 ----------  ---------------  ------------------------------------------------------------------------------------
15773 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15774                                    closes (defaults to undefined)
15775 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15776                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15777 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15778                                    progress and wait dialogs will ignore this property and always hide the
15779                                    close button as they can only be closed programmatically.
15780 cls               String           A custom CSS class to apply to the message box element
15781 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15782                                    displayed (defaults to 75)
15783 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15784                                    function will be btn (the name of the button that was clicked, if applicable,
15785                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15786                                    Progress and wait dialogs will ignore this option since they do not respond to
15787                                    user actions and can only be closed programmatically, so any required function
15788                                    should be called by the same code after it closes the dialog.
15789 icon              String           A CSS class that provides a background image to be used as an icon for
15790                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15791 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15792 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15793 modal             Boolean          False to allow user interaction with the page while the message box is
15794                                    displayed (defaults to true)
15795 msg               String           A string that will replace the existing message box body text (defaults
15796                                    to the XHTML-compliant non-breaking space character '&#160;')
15797 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15798 progress          Boolean          True to display a progress bar (defaults to false)
15799 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15800 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15801 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15802 title             String           The title text
15803 value             String           The string value to set into the active textbox element if displayed
15804 wait              Boolean          True to display a progress bar (defaults to false)
15805 width             Number           The width of the dialog in pixels
15806 </pre>
15807          *
15808          * Example usage:
15809          * <pre><code>
15810 Roo.Msg.show({
15811    title: 'Address',
15812    msg: 'Please enter your address:',
15813    width: 300,
15814    buttons: Roo.MessageBox.OKCANCEL,
15815    multiline: true,
15816    fn: saveAddress,
15817    animEl: 'addAddressBtn'
15818 });
15819 </code></pre>
15820          * @param {Object} config Configuration options
15821          * @return {Roo.MessageBox} This message box
15822          */
15823         show : function(options)
15824         {
15825             
15826             // this causes nightmares if you show one dialog after another
15827             // especially on callbacks..
15828              
15829             if(this.isVisible()){
15830                 
15831                 this.hide();
15832                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15833                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15834                 Roo.log("New Dialog Message:" +  options.msg )
15835                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15836                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15837                 
15838             }
15839             var d = this.getDialog();
15840             opt = options;
15841             d.setTitle(opt.title || "&#160;");
15842             d.close.setDisplayed(opt.closable !== false);
15843             activeTextEl = textboxEl;
15844             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15845             if(opt.prompt){
15846                 if(opt.multiline){
15847                     textboxEl.hide();
15848                     textareaEl.show();
15849                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15850                         opt.multiline : this.defaultTextHeight);
15851                     activeTextEl = textareaEl;
15852                 }else{
15853                     textboxEl.show();
15854                     textareaEl.hide();
15855                 }
15856             }else{
15857                 textboxEl.hide();
15858                 textareaEl.hide();
15859             }
15860             progressEl.setDisplayed(opt.progress === true);
15861             this.updateProgress(0);
15862             activeTextEl.dom.value = opt.value || "";
15863             if(opt.prompt){
15864                 dlg.setDefaultButton(activeTextEl);
15865             }else{
15866                 var bs = opt.buttons;
15867                 var db = null;
15868                 if(bs && bs.ok){
15869                     db = buttons["ok"];
15870                 }else if(bs && bs.yes){
15871                     db = buttons["yes"];
15872                 }
15873                 dlg.setDefaultButton(db);
15874             }
15875             bwidth = updateButtons(opt.buttons);
15876             this.updateText(opt.msg);
15877             if(opt.cls){
15878                 d.el.addClass(opt.cls);
15879             }
15880             d.proxyDrag = opt.proxyDrag === true;
15881             d.modal = opt.modal !== false;
15882             d.mask = opt.modal !== false ? mask : false;
15883             if(!d.isVisible()){
15884                 // force it to the end of the z-index stack so it gets a cursor in FF
15885                 document.body.appendChild(dlg.el.dom);
15886                 d.animateTarget = null;
15887                 d.show(options.animEl);
15888             }
15889             return this;
15890         },
15891
15892         /**
15893          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15894          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15895          * and closing the message box when the process is complete.
15896          * @param {String} title The title bar text
15897          * @param {String} msg The message box body text
15898          * @return {Roo.MessageBox} This message box
15899          */
15900         progress : function(title, msg){
15901             this.show({
15902                 title : title,
15903                 msg : msg,
15904                 buttons: false,
15905                 progress:true,
15906                 closable:false,
15907                 minWidth: this.minProgressWidth,
15908                 modal : true
15909             });
15910             return this;
15911         },
15912
15913         /**
15914          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15915          * If a callback function is passed it will be called after the user clicks the button, and the
15916          * id of the button that was clicked will be passed as the only parameter to the callback
15917          * (could also be the top-right close button).
15918          * @param {String} title The title bar text
15919          * @param {String} msg The message box body text
15920          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15921          * @param {Object} scope (optional) The scope of the callback function
15922          * @return {Roo.MessageBox} This message box
15923          */
15924         alert : function(title, msg, fn, scope){
15925             this.show({
15926                 title : title,
15927                 msg : msg,
15928                 buttons: this.OK,
15929                 fn: fn,
15930                 scope : scope,
15931                 modal : true
15932             });
15933             return this;
15934         },
15935
15936         /**
15937          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15938          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15939          * You are responsible for closing the message box when the process is complete.
15940          * @param {String} msg The message box body text
15941          * @param {String} title (optional) The title bar text
15942          * @return {Roo.MessageBox} This message box
15943          */
15944         wait : function(msg, title){
15945             this.show({
15946                 title : title,
15947                 msg : msg,
15948                 buttons: false,
15949                 closable:false,
15950                 progress:true,
15951                 modal:true,
15952                 width:300,
15953                 wait:true
15954             });
15955             waitTimer = Roo.TaskMgr.start({
15956                 run: function(i){
15957                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15958                 },
15959                 interval: 1000
15960             });
15961             return this;
15962         },
15963
15964         /**
15965          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15966          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15967          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15968          * @param {String} title The title bar text
15969          * @param {String} msg The message box body text
15970          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15971          * @param {Object} scope (optional) The scope of the callback function
15972          * @return {Roo.MessageBox} This message box
15973          */
15974         confirm : function(title, msg, fn, scope){
15975             this.show({
15976                 title : title,
15977                 msg : msg,
15978                 buttons: this.YESNO,
15979                 fn: fn,
15980                 scope : scope,
15981                 modal : true
15982             });
15983             return this;
15984         },
15985
15986         /**
15987          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15988          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15989          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15990          * (could also be the top-right close button) and the text that was entered will be passed as the two
15991          * parameters to the callback.
15992          * @param {String} title The title bar text
15993          * @param {String} msg The message box body text
15994          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15995          * @param {Object} scope (optional) The scope of the callback function
15996          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15997          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15998          * @return {Roo.MessageBox} This message box
15999          */
16000         prompt : function(title, msg, fn, scope, multiline){
16001             this.show({
16002                 title : title,
16003                 msg : msg,
16004                 buttons: this.OKCANCEL,
16005                 fn: fn,
16006                 minWidth:250,
16007                 scope : scope,
16008                 prompt:true,
16009                 multiline: multiline,
16010                 modal : true
16011             });
16012             return this;
16013         },
16014
16015         /**
16016          * Button config that displays a single OK button
16017          * @type Object
16018          */
16019         OK : {ok:true},
16020         /**
16021          * Button config that displays Yes and No buttons
16022          * @type Object
16023          */
16024         YESNO : {yes:true, no:true},
16025         /**
16026          * Button config that displays OK and Cancel buttons
16027          * @type Object
16028          */
16029         OKCANCEL : {ok:true, cancel:true},
16030         /**
16031          * Button config that displays Yes, No and Cancel buttons
16032          * @type Object
16033          */
16034         YESNOCANCEL : {yes:true, no:true, cancel:true},
16035
16036         /**
16037          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16038          * @type Number
16039          */
16040         defaultTextHeight : 75,
16041         /**
16042          * The maximum width in pixels of the message box (defaults to 600)
16043          * @type Number
16044          */
16045         maxWidth : 600,
16046         /**
16047          * The minimum width in pixels of the message box (defaults to 100)
16048          * @type Number
16049          */
16050         minWidth : 100,
16051         /**
16052          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16053          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16054          * @type Number
16055          */
16056         minProgressWidth : 250,
16057         /**
16058          * An object containing the default button text strings that can be overriden for localized language support.
16059          * Supported properties are: ok, cancel, yes and no.
16060          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16061          * @type Object
16062          */
16063         buttonText : {
16064             ok : "OK",
16065             cancel : "Cancel",
16066             yes : "Yes",
16067             no : "No"
16068         }
16069     };
16070 }();
16071
16072 /**
16073  * Shorthand for {@link Roo.MessageBox}
16074  */
16075 Roo.Msg = Roo.MessageBox;/*
16076  * Based on:
16077  * Ext JS Library 1.1.1
16078  * Copyright(c) 2006-2007, Ext JS, LLC.
16079  *
16080  * Originally Released Under LGPL - original licence link has changed is not relivant.
16081  *
16082  * Fork - LGPL
16083  * <script type="text/javascript">
16084  */
16085 /**
16086  * @class Roo.QuickTips
16087  * Provides attractive and customizable tooltips for any element.
16088  * @singleton
16089  */
16090 Roo.QuickTips = function(){
16091     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16092     var ce, bd, xy, dd;
16093     var visible = false, disabled = true, inited = false;
16094     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16095     
16096     var onOver = function(e){
16097         if(disabled){
16098             return;
16099         }
16100         var t = e.getTarget();
16101         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16102             return;
16103         }
16104         if(ce && t == ce.el){
16105             clearTimeout(hideProc);
16106             return;
16107         }
16108         if(t && tagEls[t.id]){
16109             tagEls[t.id].el = t;
16110             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16111             return;
16112         }
16113         var ttp, et = Roo.fly(t);
16114         var ns = cfg.namespace;
16115         if(tm.interceptTitles && t.title){
16116             ttp = t.title;
16117             t.qtip = ttp;
16118             t.removeAttribute("title");
16119             e.preventDefault();
16120         }else{
16121             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16122         }
16123         if(ttp){
16124             showProc = show.defer(tm.showDelay, tm, [{
16125                 el: t, 
16126                 text: ttp, 
16127                 width: et.getAttributeNS(ns, cfg.width),
16128                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16129                 title: et.getAttributeNS(ns, cfg.title),
16130                     cls: et.getAttributeNS(ns, cfg.cls)
16131             }]);
16132         }
16133     };
16134     
16135     var onOut = function(e){
16136         clearTimeout(showProc);
16137         var t = e.getTarget();
16138         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16139             hideProc = setTimeout(hide, tm.hideDelay);
16140         }
16141     };
16142     
16143     var onMove = function(e){
16144         if(disabled){
16145             return;
16146         }
16147         xy = e.getXY();
16148         xy[1] += 18;
16149         if(tm.trackMouse && ce){
16150             el.setXY(xy);
16151         }
16152     };
16153     
16154     var onDown = function(e){
16155         clearTimeout(showProc);
16156         clearTimeout(hideProc);
16157         if(!e.within(el)){
16158             if(tm.hideOnClick){
16159                 hide();
16160                 tm.disable();
16161                 tm.enable.defer(100, tm);
16162             }
16163         }
16164     };
16165     
16166     var getPad = function(){
16167         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16168     };
16169
16170     var show = function(o){
16171         if(disabled){
16172             return;
16173         }
16174         clearTimeout(dismissProc);
16175         ce = o;
16176         if(removeCls){ // in case manually hidden
16177             el.removeClass(removeCls);
16178             removeCls = null;
16179         }
16180         if(ce.cls){
16181             el.addClass(ce.cls);
16182             removeCls = ce.cls;
16183         }
16184         if(ce.title){
16185             tipTitle.update(ce.title);
16186             tipTitle.show();
16187         }else{
16188             tipTitle.update('');
16189             tipTitle.hide();
16190         }
16191         el.dom.style.width  = tm.maxWidth+'px';
16192         //tipBody.dom.style.width = '';
16193         tipBodyText.update(o.text);
16194         var p = getPad(), w = ce.width;
16195         if(!w){
16196             var td = tipBodyText.dom;
16197             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16198             if(aw > tm.maxWidth){
16199                 w = tm.maxWidth;
16200             }else if(aw < tm.minWidth){
16201                 w = tm.minWidth;
16202             }else{
16203                 w = aw;
16204             }
16205         }
16206         //tipBody.setWidth(w);
16207         el.setWidth(parseInt(w, 10) + p);
16208         if(ce.autoHide === false){
16209             close.setDisplayed(true);
16210             if(dd){
16211                 dd.unlock();
16212             }
16213         }else{
16214             close.setDisplayed(false);
16215             if(dd){
16216                 dd.lock();
16217             }
16218         }
16219         if(xy){
16220             el.avoidY = xy[1]-18;
16221             el.setXY(xy);
16222         }
16223         if(tm.animate){
16224             el.setOpacity(.1);
16225             el.setStyle("visibility", "visible");
16226             el.fadeIn({callback: afterShow});
16227         }else{
16228             afterShow();
16229         }
16230     };
16231     
16232     var afterShow = function(){
16233         if(ce){
16234             el.show();
16235             esc.enable();
16236             if(tm.autoDismiss && ce.autoHide !== false){
16237                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16238             }
16239         }
16240     };
16241     
16242     var hide = function(noanim){
16243         clearTimeout(dismissProc);
16244         clearTimeout(hideProc);
16245         ce = null;
16246         if(el.isVisible()){
16247             esc.disable();
16248             if(noanim !== true && tm.animate){
16249                 el.fadeOut({callback: afterHide});
16250             }else{
16251                 afterHide();
16252             } 
16253         }
16254     };
16255     
16256     var afterHide = function(){
16257         el.hide();
16258         if(removeCls){
16259             el.removeClass(removeCls);
16260             removeCls = null;
16261         }
16262     };
16263     
16264     return {
16265         /**
16266         * @cfg {Number} minWidth
16267         * The minimum width of the quick tip (defaults to 40)
16268         */
16269        minWidth : 40,
16270         /**
16271         * @cfg {Number} maxWidth
16272         * The maximum width of the quick tip (defaults to 300)
16273         */
16274        maxWidth : 300,
16275         /**
16276         * @cfg {Boolean} interceptTitles
16277         * True to automatically use the element's DOM title value if available (defaults to false)
16278         */
16279        interceptTitles : false,
16280         /**
16281         * @cfg {Boolean} trackMouse
16282         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16283         */
16284        trackMouse : false,
16285         /**
16286         * @cfg {Boolean} hideOnClick
16287         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16288         */
16289        hideOnClick : true,
16290         /**
16291         * @cfg {Number} showDelay
16292         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16293         */
16294        showDelay : 500,
16295         /**
16296         * @cfg {Number} hideDelay
16297         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16298         */
16299        hideDelay : 200,
16300         /**
16301         * @cfg {Boolean} autoHide
16302         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16303         * Used in conjunction with hideDelay.
16304         */
16305        autoHide : true,
16306         /**
16307         * @cfg {Boolean}
16308         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16309         * (defaults to true).  Used in conjunction with autoDismissDelay.
16310         */
16311        autoDismiss : true,
16312         /**
16313         * @cfg {Number}
16314         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16315         */
16316        autoDismissDelay : 5000,
16317        /**
16318         * @cfg {Boolean} animate
16319         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16320         */
16321        animate : false,
16322
16323        /**
16324         * @cfg {String} title
16325         * Title text to display (defaults to '').  This can be any valid HTML markup.
16326         */
16327         title: '',
16328        /**
16329         * @cfg {String} text
16330         * Body text to display (defaults to '').  This can be any valid HTML markup.
16331         */
16332         text : '',
16333        /**
16334         * @cfg {String} cls
16335         * A CSS class to apply to the base quick tip element (defaults to '').
16336         */
16337         cls : '',
16338        /**
16339         * @cfg {Number} width
16340         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16341         * minWidth or maxWidth.
16342         */
16343         width : null,
16344
16345     /**
16346      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16347      * or display QuickTips in a page.
16348      */
16349        init : function(){
16350           tm = Roo.QuickTips;
16351           cfg = tm.tagConfig;
16352           if(!inited){
16353               if(!Roo.isReady){ // allow calling of init() before onReady
16354                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16355                   return;
16356               }
16357               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16358               el.fxDefaults = {stopFx: true};
16359               // maximum custom styling
16360               //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>');
16361               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>');              
16362               tipTitle = el.child('h3');
16363               tipTitle.enableDisplayMode("block");
16364               tipBody = el.child('div.x-tip-bd');
16365               tipBodyText = el.child('div.x-tip-bd-inner');
16366               //bdLeft = el.child('div.x-tip-bd-left');
16367               //bdRight = el.child('div.x-tip-bd-right');
16368               close = el.child('div.x-tip-close');
16369               close.enableDisplayMode("block");
16370               close.on("click", hide);
16371               var d = Roo.get(document);
16372               d.on("mousedown", onDown);
16373               d.on("mouseover", onOver);
16374               d.on("mouseout", onOut);
16375               d.on("mousemove", onMove);
16376               esc = d.addKeyListener(27, hide);
16377               esc.disable();
16378               if(Roo.dd.DD){
16379                   dd = el.initDD("default", null, {
16380                       onDrag : function(){
16381                           el.sync();  
16382                       }
16383                   });
16384                   dd.setHandleElId(tipTitle.id);
16385                   dd.lock();
16386               }
16387               inited = true;
16388           }
16389           this.enable(); 
16390        },
16391
16392     /**
16393      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16394      * are supported:
16395      * <pre>
16396 Property    Type                   Description
16397 ----------  ---------------------  ------------------------------------------------------------------------
16398 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16399      * </ul>
16400      * @param {Object} config The config object
16401      */
16402        register : function(config){
16403            var cs = config instanceof Array ? config : arguments;
16404            for(var i = 0, len = cs.length; i < len; i++) {
16405                var c = cs[i];
16406                var target = c.target;
16407                if(target){
16408                    if(target instanceof Array){
16409                        for(var j = 0, jlen = target.length; j < jlen; j++){
16410                            tagEls[target[j]] = c;
16411                        }
16412                    }else{
16413                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16414                    }
16415                }
16416            }
16417        },
16418
16419     /**
16420      * Removes this quick tip from its element and destroys it.
16421      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16422      */
16423        unregister : function(el){
16424            delete tagEls[Roo.id(el)];
16425        },
16426
16427     /**
16428      * Enable this quick tip.
16429      */
16430        enable : function(){
16431            if(inited && disabled){
16432                locks.pop();
16433                if(locks.length < 1){
16434                    disabled = false;
16435                }
16436            }
16437        },
16438
16439     /**
16440      * Disable this quick tip.
16441      */
16442        disable : function(){
16443           disabled = true;
16444           clearTimeout(showProc);
16445           clearTimeout(hideProc);
16446           clearTimeout(dismissProc);
16447           if(ce){
16448               hide(true);
16449           }
16450           locks.push(1);
16451        },
16452
16453     /**
16454      * Returns true if the quick tip is enabled, else false.
16455      */
16456        isEnabled : function(){
16457             return !disabled;
16458        },
16459
16460         // private
16461        tagConfig : {
16462            namespace : "ext",
16463            attribute : "qtip",
16464            width : "width",
16465            target : "target",
16466            title : "qtitle",
16467            hide : "hide",
16468            cls : "qclass"
16469        }
16470    };
16471 }();
16472
16473 // backwards compat
16474 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16475  * Based on:
16476  * Ext JS Library 1.1.1
16477  * Copyright(c) 2006-2007, Ext JS, LLC.
16478  *
16479  * Originally Released Under LGPL - original licence link has changed is not relivant.
16480  *
16481  * Fork - LGPL
16482  * <script type="text/javascript">
16483  */
16484  
16485
16486 /**
16487  * @class Roo.tree.TreePanel
16488  * @extends Roo.data.Tree
16489
16490  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16491  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16492  * @cfg {Boolean} enableDD true to enable drag and drop
16493  * @cfg {Boolean} enableDrag true to enable just drag
16494  * @cfg {Boolean} enableDrop true to enable just drop
16495  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16496  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16497  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16498  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16499  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16500  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16501  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16502  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16503  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16504  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16505  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16506  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16507  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16508  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16509  * @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>
16510  * @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>
16511  * 
16512  * @constructor
16513  * @param {String/HTMLElement/Element} el The container element
16514  * @param {Object} config
16515  */
16516 Roo.tree.TreePanel = function(el, config){
16517     var root = false;
16518     var loader = false;
16519     if (config.root) {
16520         root = config.root;
16521         delete config.root;
16522     }
16523     if (config.loader) {
16524         loader = config.loader;
16525         delete config.loader;
16526     }
16527     
16528     Roo.apply(this, config);
16529     Roo.tree.TreePanel.superclass.constructor.call(this);
16530     this.el = Roo.get(el);
16531     this.el.addClass('x-tree');
16532     //console.log(root);
16533     if (root) {
16534         this.setRootNode( Roo.factory(root, Roo.tree));
16535     }
16536     if (loader) {
16537         this.loader = Roo.factory(loader, Roo.tree);
16538     }
16539    /**
16540     * Read-only. The id of the container element becomes this TreePanel's id.
16541     */
16542     this.id = this.el.id;
16543     this.addEvents({
16544         /**
16545         * @event beforeload
16546         * Fires before a node is loaded, return false to cancel
16547         * @param {Node} node The node being loaded
16548         */
16549         "beforeload" : true,
16550         /**
16551         * @event load
16552         * Fires when a node is loaded
16553         * @param {Node} node The node that was loaded
16554         */
16555         "load" : true,
16556         /**
16557         * @event textchange
16558         * Fires when the text for a node is changed
16559         * @param {Node} node The node
16560         * @param {String} text The new text
16561         * @param {String} oldText The old text
16562         */
16563         "textchange" : true,
16564         /**
16565         * @event beforeexpand
16566         * Fires before a node is expanded, return false to cancel.
16567         * @param {Node} node The node
16568         * @param {Boolean} deep
16569         * @param {Boolean} anim
16570         */
16571         "beforeexpand" : true,
16572         /**
16573         * @event beforecollapse
16574         * Fires before a node is collapsed, return false to cancel.
16575         * @param {Node} node The node
16576         * @param {Boolean} deep
16577         * @param {Boolean} anim
16578         */
16579         "beforecollapse" : true,
16580         /**
16581         * @event expand
16582         * Fires when a node is expanded
16583         * @param {Node} node The node
16584         */
16585         "expand" : true,
16586         /**
16587         * @event disabledchange
16588         * Fires when the disabled status of a node changes
16589         * @param {Node} node The node
16590         * @param {Boolean} disabled
16591         */
16592         "disabledchange" : true,
16593         /**
16594         * @event collapse
16595         * Fires when a node is collapsed
16596         * @param {Node} node The node
16597         */
16598         "collapse" : true,
16599         /**
16600         * @event beforeclick
16601         * Fires before click processing on a node. Return false to cancel the default action.
16602         * @param {Node} node The node
16603         * @param {Roo.EventObject} e The event object
16604         */
16605         "beforeclick":true,
16606         /**
16607         * @event checkchange
16608         * Fires when a node with a checkbox's checked property changes
16609         * @param {Node} this This node
16610         * @param {Boolean} checked
16611         */
16612         "checkchange":true,
16613         /**
16614         * @event click
16615         * Fires when a node is clicked
16616         * @param {Node} node The node
16617         * @param {Roo.EventObject} e The event object
16618         */
16619         "click":true,
16620         /**
16621         * @event dblclick
16622         * Fires when a node is double clicked
16623         * @param {Node} node The node
16624         * @param {Roo.EventObject} e The event object
16625         */
16626         "dblclick":true,
16627         /**
16628         * @event contextmenu
16629         * Fires when a node is right clicked
16630         * @param {Node} node The node
16631         * @param {Roo.EventObject} e The event object
16632         */
16633         "contextmenu":true,
16634         /**
16635         * @event beforechildrenrendered
16636         * Fires right before the child nodes for a node are rendered
16637         * @param {Node} node The node
16638         */
16639         "beforechildrenrendered":true,
16640         /**
16641         * @event startdrag
16642         * Fires when a node starts being dragged
16643         * @param {Roo.tree.TreePanel} this
16644         * @param {Roo.tree.TreeNode} node
16645         * @param {event} e The raw browser event
16646         */ 
16647        "startdrag" : true,
16648        /**
16649         * @event enddrag
16650         * Fires when a drag operation is complete
16651         * @param {Roo.tree.TreePanel} this
16652         * @param {Roo.tree.TreeNode} node
16653         * @param {event} e The raw browser event
16654         */
16655        "enddrag" : true,
16656        /**
16657         * @event dragdrop
16658         * Fires when a dragged node is dropped on a valid DD target
16659         * @param {Roo.tree.TreePanel} this
16660         * @param {Roo.tree.TreeNode} node
16661         * @param {DD} dd The dd it was dropped on
16662         * @param {event} e The raw browser event
16663         */
16664        "dragdrop" : true,
16665        /**
16666         * @event beforenodedrop
16667         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16668         * passed to handlers has the following properties:<br />
16669         * <ul style="padding:5px;padding-left:16px;">
16670         * <li>tree - The TreePanel</li>
16671         * <li>target - The node being targeted for the drop</li>
16672         * <li>data - The drag data from the drag source</li>
16673         * <li>point - The point of the drop - append, above or below</li>
16674         * <li>source - The drag source</li>
16675         * <li>rawEvent - Raw mouse event</li>
16676         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16677         * to be inserted by setting them on this object.</li>
16678         * <li>cancel - Set this to true to cancel the drop.</li>
16679         * </ul>
16680         * @param {Object} dropEvent
16681         */
16682        "beforenodedrop" : true,
16683        /**
16684         * @event nodedrop
16685         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16686         * passed to handlers has the following properties:<br />
16687         * <ul style="padding:5px;padding-left:16px;">
16688         * <li>tree - The TreePanel</li>
16689         * <li>target - The node being targeted for the drop</li>
16690         * <li>data - The drag data from the drag source</li>
16691         * <li>point - The point of the drop - append, above or below</li>
16692         * <li>source - The drag source</li>
16693         * <li>rawEvent - Raw mouse event</li>
16694         * <li>dropNode - Dropped node(s).</li>
16695         * </ul>
16696         * @param {Object} dropEvent
16697         */
16698        "nodedrop" : true,
16699         /**
16700         * @event nodedragover
16701         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16702         * passed to handlers has the following properties:<br />
16703         * <ul style="padding:5px;padding-left:16px;">
16704         * <li>tree - The TreePanel</li>
16705         * <li>target - The node being targeted for the drop</li>
16706         * <li>data - The drag data from the drag source</li>
16707         * <li>point - The point of the drop - append, above or below</li>
16708         * <li>source - The drag source</li>
16709         * <li>rawEvent - Raw mouse event</li>
16710         * <li>dropNode - Drop node(s) provided by the source.</li>
16711         * <li>cancel - Set this to true to signal drop not allowed.</li>
16712         * </ul>
16713         * @param {Object} dragOverEvent
16714         */
16715        "nodedragover" : true
16716         
16717     });
16718     if(this.singleExpand){
16719        this.on("beforeexpand", this.restrictExpand, this);
16720     }
16721     if (this.editor) {
16722         this.editor.tree = this;
16723         this.editor = Roo.factory(this.editor, Roo.tree);
16724     }
16725     
16726     if (this.selModel) {
16727         this.selModel = Roo.factory(this.selModel, Roo.tree);
16728     }
16729    
16730 };
16731 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16732     rootVisible : true,
16733     animate: Roo.enableFx,
16734     lines : true,
16735     enableDD : false,
16736     hlDrop : Roo.enableFx,
16737   
16738     renderer: false,
16739     
16740     rendererTip: false,
16741     // private
16742     restrictExpand : function(node){
16743         var p = node.parentNode;
16744         if(p){
16745             if(p.expandedChild && p.expandedChild.parentNode == p){
16746                 p.expandedChild.collapse();
16747             }
16748             p.expandedChild = node;
16749         }
16750     },
16751
16752     // private override
16753     setRootNode : function(node){
16754         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16755         if(!this.rootVisible){
16756             node.ui = new Roo.tree.RootTreeNodeUI(node);
16757         }
16758         return node;
16759     },
16760
16761     /**
16762      * Returns the container element for this TreePanel
16763      */
16764     getEl : function(){
16765         return this.el;
16766     },
16767
16768     /**
16769      * Returns the default TreeLoader for this TreePanel
16770      */
16771     getLoader : function(){
16772         return this.loader;
16773     },
16774
16775     /**
16776      * Expand all nodes
16777      */
16778     expandAll : function(){
16779         this.root.expand(true);
16780     },
16781
16782     /**
16783      * Collapse all nodes
16784      */
16785     collapseAll : function(){
16786         this.root.collapse(true);
16787     },
16788
16789     /**
16790      * Returns the selection model used by this TreePanel
16791      */
16792     getSelectionModel : function(){
16793         if(!this.selModel){
16794             this.selModel = new Roo.tree.DefaultSelectionModel();
16795         }
16796         return this.selModel;
16797     },
16798
16799     /**
16800      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16801      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16802      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16803      * @return {Array}
16804      */
16805     getChecked : function(a, startNode){
16806         startNode = startNode || this.root;
16807         var r = [];
16808         var f = function(){
16809             if(this.attributes.checked){
16810                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16811             }
16812         }
16813         startNode.cascade(f);
16814         return r;
16815     },
16816
16817     /**
16818      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16819      * @param {String} path
16820      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16821      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16822      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16823      */
16824     expandPath : function(path, attr, callback){
16825         attr = attr || "id";
16826         var keys = path.split(this.pathSeparator);
16827         var curNode = this.root;
16828         if(curNode.attributes[attr] != keys[1]){ // invalid root
16829             if(callback){
16830                 callback(false, null);
16831             }
16832             return;
16833         }
16834         var index = 1;
16835         var f = function(){
16836             if(++index == keys.length){
16837                 if(callback){
16838                     callback(true, curNode);
16839                 }
16840                 return;
16841             }
16842             var c = curNode.findChild(attr, keys[index]);
16843             if(!c){
16844                 if(callback){
16845                     callback(false, curNode);
16846                 }
16847                 return;
16848             }
16849             curNode = c;
16850             c.expand(false, false, f);
16851         };
16852         curNode.expand(false, false, f);
16853     },
16854
16855     /**
16856      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16857      * @param {String} path
16858      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16859      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16860      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16861      */
16862     selectPath : function(path, attr, callback){
16863         attr = attr || "id";
16864         var keys = path.split(this.pathSeparator);
16865         var v = keys.pop();
16866         if(keys.length > 0){
16867             var f = function(success, node){
16868                 if(success && node){
16869                     var n = node.findChild(attr, v);
16870                     if(n){
16871                         n.select();
16872                         if(callback){
16873                             callback(true, n);
16874                         }
16875                     }else if(callback){
16876                         callback(false, n);
16877                     }
16878                 }else{
16879                     if(callback){
16880                         callback(false, n);
16881                     }
16882                 }
16883             };
16884             this.expandPath(keys.join(this.pathSeparator), attr, f);
16885         }else{
16886             this.root.select();
16887             if(callback){
16888                 callback(true, this.root);
16889             }
16890         }
16891     },
16892
16893     getTreeEl : function(){
16894         return this.el;
16895     },
16896
16897     /**
16898      * Trigger rendering of this TreePanel
16899      */
16900     render : function(){
16901         if (this.innerCt) {
16902             return this; // stop it rendering more than once!!
16903         }
16904         
16905         this.innerCt = this.el.createChild({tag:"ul",
16906                cls:"x-tree-root-ct " +
16907                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16908
16909         if(this.containerScroll){
16910             Roo.dd.ScrollManager.register(this.el);
16911         }
16912         if((this.enableDD || this.enableDrop) && !this.dropZone){
16913            /**
16914             * The dropZone used by this tree if drop is enabled
16915             * @type Roo.tree.TreeDropZone
16916             */
16917              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16918                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16919            });
16920         }
16921         if((this.enableDD || this.enableDrag) && !this.dragZone){
16922            /**
16923             * The dragZone used by this tree if drag is enabled
16924             * @type Roo.tree.TreeDragZone
16925             */
16926             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16927                ddGroup: this.ddGroup || "TreeDD",
16928                scroll: this.ddScroll
16929            });
16930         }
16931         this.getSelectionModel().init(this);
16932         if (!this.root) {
16933             Roo.log("ROOT not set in tree");
16934             return this;
16935         }
16936         this.root.render();
16937         if(!this.rootVisible){
16938             this.root.renderChildren();
16939         }
16940         return this;
16941     }
16942 });/*
16943  * Based on:
16944  * Ext JS Library 1.1.1
16945  * Copyright(c) 2006-2007, Ext JS, LLC.
16946  *
16947  * Originally Released Under LGPL - original licence link has changed is not relivant.
16948  *
16949  * Fork - LGPL
16950  * <script type="text/javascript">
16951  */
16952  
16953
16954 /**
16955  * @class Roo.tree.DefaultSelectionModel
16956  * @extends Roo.util.Observable
16957  * The default single selection for a TreePanel.
16958  * @param {Object} cfg Configuration
16959  */
16960 Roo.tree.DefaultSelectionModel = function(cfg){
16961    this.selNode = null;
16962    
16963    
16964    
16965    this.addEvents({
16966        /**
16967         * @event selectionchange
16968         * Fires when the selected node changes
16969         * @param {DefaultSelectionModel} this
16970         * @param {TreeNode} node the new selection
16971         */
16972        "selectionchange" : true,
16973
16974        /**
16975         * @event beforeselect
16976         * Fires before the selected node changes, return false to cancel the change
16977         * @param {DefaultSelectionModel} this
16978         * @param {TreeNode} node the new selection
16979         * @param {TreeNode} node the old selection
16980         */
16981        "beforeselect" : true
16982    });
16983    
16984     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16985 };
16986
16987 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16988     init : function(tree){
16989         this.tree = tree;
16990         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16991         tree.on("click", this.onNodeClick, this);
16992     },
16993     
16994     onNodeClick : function(node, e){
16995         if (e.ctrlKey && this.selNode == node)  {
16996             this.unselect(node);
16997             return;
16998         }
16999         this.select(node);
17000     },
17001     
17002     /**
17003      * Select a node.
17004      * @param {TreeNode} node The node to select
17005      * @return {TreeNode} The selected node
17006      */
17007     select : function(node){
17008         var last = this.selNode;
17009         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17010             if(last){
17011                 last.ui.onSelectedChange(false);
17012             }
17013             this.selNode = node;
17014             node.ui.onSelectedChange(true);
17015             this.fireEvent("selectionchange", this, node, last);
17016         }
17017         return node;
17018     },
17019     
17020     /**
17021      * Deselect a node.
17022      * @param {TreeNode} node The node to unselect
17023      */
17024     unselect : function(node){
17025         if(this.selNode == node){
17026             this.clearSelections();
17027         }    
17028     },
17029     
17030     /**
17031      * Clear all selections
17032      */
17033     clearSelections : function(){
17034         var n = this.selNode;
17035         if(n){
17036             n.ui.onSelectedChange(false);
17037             this.selNode = null;
17038             this.fireEvent("selectionchange", this, null);
17039         }
17040         return n;
17041     },
17042     
17043     /**
17044      * Get the selected node
17045      * @return {TreeNode} The selected node
17046      */
17047     getSelectedNode : function(){
17048         return this.selNode;    
17049     },
17050     
17051     /**
17052      * Returns true if the node is selected
17053      * @param {TreeNode} node The node to check
17054      * @return {Boolean}
17055      */
17056     isSelected : function(node){
17057         return this.selNode == node;  
17058     },
17059
17060     /**
17061      * Selects the node above the selected node in the tree, intelligently walking the nodes
17062      * @return TreeNode The new selection
17063      */
17064     selectPrevious : function(){
17065         var s = this.selNode || this.lastSelNode;
17066         if(!s){
17067             return null;
17068         }
17069         var ps = s.previousSibling;
17070         if(ps){
17071             if(!ps.isExpanded() || ps.childNodes.length < 1){
17072                 return this.select(ps);
17073             } else{
17074                 var lc = ps.lastChild;
17075                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17076                     lc = lc.lastChild;
17077                 }
17078                 return this.select(lc);
17079             }
17080         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17081             return this.select(s.parentNode);
17082         }
17083         return null;
17084     },
17085
17086     /**
17087      * Selects the node above the selected node in the tree, intelligently walking the nodes
17088      * @return TreeNode The new selection
17089      */
17090     selectNext : function(){
17091         var s = this.selNode || this.lastSelNode;
17092         if(!s){
17093             return null;
17094         }
17095         if(s.firstChild && s.isExpanded()){
17096              return this.select(s.firstChild);
17097          }else if(s.nextSibling){
17098              return this.select(s.nextSibling);
17099          }else if(s.parentNode){
17100             var newS = null;
17101             s.parentNode.bubble(function(){
17102                 if(this.nextSibling){
17103                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17104                     return false;
17105                 }
17106             });
17107             return newS;
17108          }
17109         return null;
17110     },
17111
17112     onKeyDown : function(e){
17113         var s = this.selNode || this.lastSelNode;
17114         // undesirable, but required
17115         var sm = this;
17116         if(!s){
17117             return;
17118         }
17119         var k = e.getKey();
17120         switch(k){
17121              case e.DOWN:
17122                  e.stopEvent();
17123                  this.selectNext();
17124              break;
17125              case e.UP:
17126                  e.stopEvent();
17127                  this.selectPrevious();
17128              break;
17129              case e.RIGHT:
17130                  e.preventDefault();
17131                  if(s.hasChildNodes()){
17132                      if(!s.isExpanded()){
17133                          s.expand();
17134                      }else if(s.firstChild){
17135                          this.select(s.firstChild, e);
17136                      }
17137                  }
17138              break;
17139              case e.LEFT:
17140                  e.preventDefault();
17141                  if(s.hasChildNodes() && s.isExpanded()){
17142                      s.collapse();
17143                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17144                      this.select(s.parentNode, e);
17145                  }
17146              break;
17147         };
17148     }
17149 });
17150
17151 /**
17152  * @class Roo.tree.MultiSelectionModel
17153  * @extends Roo.util.Observable
17154  * Multi selection for a TreePanel.
17155  * @param {Object} cfg Configuration
17156  */
17157 Roo.tree.MultiSelectionModel = function(){
17158    this.selNodes = [];
17159    this.selMap = {};
17160    this.addEvents({
17161        /**
17162         * @event selectionchange
17163         * Fires when the selected nodes change
17164         * @param {MultiSelectionModel} this
17165         * @param {Array} nodes Array of the selected nodes
17166         */
17167        "selectionchange" : true
17168    });
17169    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17170    
17171 };
17172
17173 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17174     init : function(tree){
17175         this.tree = tree;
17176         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17177         tree.on("click", this.onNodeClick, this);
17178     },
17179     
17180     onNodeClick : function(node, e){
17181         this.select(node, e, e.ctrlKey);
17182     },
17183     
17184     /**
17185      * Select a node.
17186      * @param {TreeNode} node The node to select
17187      * @param {EventObject} e (optional) An event associated with the selection
17188      * @param {Boolean} keepExisting True to retain existing selections
17189      * @return {TreeNode} The selected node
17190      */
17191     select : function(node, e, keepExisting){
17192         if(keepExisting !== true){
17193             this.clearSelections(true);
17194         }
17195         if(this.isSelected(node)){
17196             this.lastSelNode = node;
17197             return node;
17198         }
17199         this.selNodes.push(node);
17200         this.selMap[node.id] = node;
17201         this.lastSelNode = node;
17202         node.ui.onSelectedChange(true);
17203         this.fireEvent("selectionchange", this, this.selNodes);
17204         return node;
17205     },
17206     
17207     /**
17208      * Deselect a node.
17209      * @param {TreeNode} node The node to unselect
17210      */
17211     unselect : function(node){
17212         if(this.selMap[node.id]){
17213             node.ui.onSelectedChange(false);
17214             var sn = this.selNodes;
17215             var index = -1;
17216             if(sn.indexOf){
17217                 index = sn.indexOf(node);
17218             }else{
17219                 for(var i = 0, len = sn.length; i < len; i++){
17220                     if(sn[i] == node){
17221                         index = i;
17222                         break;
17223                     }
17224                 }
17225             }
17226             if(index != -1){
17227                 this.selNodes.splice(index, 1);
17228             }
17229             delete this.selMap[node.id];
17230             this.fireEvent("selectionchange", this, this.selNodes);
17231         }
17232     },
17233     
17234     /**
17235      * Clear all selections
17236      */
17237     clearSelections : function(suppressEvent){
17238         var sn = this.selNodes;
17239         if(sn.length > 0){
17240             for(var i = 0, len = sn.length; i < len; i++){
17241                 sn[i].ui.onSelectedChange(false);
17242             }
17243             this.selNodes = [];
17244             this.selMap = {};
17245             if(suppressEvent !== true){
17246                 this.fireEvent("selectionchange", this, this.selNodes);
17247             }
17248         }
17249     },
17250     
17251     /**
17252      * Returns true if the node is selected
17253      * @param {TreeNode} node The node to check
17254      * @return {Boolean}
17255      */
17256     isSelected : function(node){
17257         return this.selMap[node.id] ? true : false;  
17258     },
17259     
17260     /**
17261      * Returns an array of the selected nodes
17262      * @return {Array}
17263      */
17264     getSelectedNodes : function(){
17265         return this.selNodes;    
17266     },
17267
17268     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17269
17270     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17271
17272     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17273 });/*
17274  * Based on:
17275  * Ext JS Library 1.1.1
17276  * Copyright(c) 2006-2007, Ext JS, LLC.
17277  *
17278  * Originally Released Under LGPL - original licence link has changed is not relivant.
17279  *
17280  * Fork - LGPL
17281  * <script type="text/javascript">
17282  */
17283  
17284 /**
17285  * @class Roo.tree.TreeNode
17286  * @extends Roo.data.Node
17287  * @cfg {String} text The text for this node
17288  * @cfg {Boolean} expanded true to start the node expanded
17289  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17290  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17291  * @cfg {Boolean} disabled true to start the node disabled
17292  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17293  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17294  * @cfg {String} cls A css class to be added to the node
17295  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17296  * @cfg {String} href URL of the link used for the node (defaults to #)
17297  * @cfg {String} hrefTarget target frame for the link
17298  * @cfg {String} qtip An Ext QuickTip for the node
17299  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17300  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17301  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17302  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17303  * (defaults to undefined with no checkbox rendered)
17304  * @constructor
17305  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17306  */
17307 Roo.tree.TreeNode = function(attributes){
17308     attributes = attributes || {};
17309     if(typeof attributes == "string"){
17310         attributes = {text: attributes};
17311     }
17312     this.childrenRendered = false;
17313     this.rendered = false;
17314     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17315     this.expanded = attributes.expanded === true;
17316     this.isTarget = attributes.isTarget !== false;
17317     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17318     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17319
17320     /**
17321      * Read-only. The text for this node. To change it use setText().
17322      * @type String
17323      */
17324     this.text = attributes.text;
17325     /**
17326      * True if this node is disabled.
17327      * @type Boolean
17328      */
17329     this.disabled = attributes.disabled === true;
17330
17331     this.addEvents({
17332         /**
17333         * @event textchange
17334         * Fires when the text for this node is changed
17335         * @param {Node} this This node
17336         * @param {String} text The new text
17337         * @param {String} oldText The old text
17338         */
17339         "textchange" : true,
17340         /**
17341         * @event beforeexpand
17342         * Fires before this node is expanded, return false to cancel.
17343         * @param {Node} this This node
17344         * @param {Boolean} deep
17345         * @param {Boolean} anim
17346         */
17347         "beforeexpand" : true,
17348         /**
17349         * @event beforecollapse
17350         * Fires before this node is collapsed, return false to cancel.
17351         * @param {Node} this This node
17352         * @param {Boolean} deep
17353         * @param {Boolean} anim
17354         */
17355         "beforecollapse" : true,
17356         /**
17357         * @event expand
17358         * Fires when this node is expanded
17359         * @param {Node} this This node
17360         */
17361         "expand" : true,
17362         /**
17363         * @event disabledchange
17364         * Fires when the disabled status of this node changes
17365         * @param {Node} this This node
17366         * @param {Boolean} disabled
17367         */
17368         "disabledchange" : true,
17369         /**
17370         * @event collapse
17371         * Fires when this node is collapsed
17372         * @param {Node} this This node
17373         */
17374         "collapse" : true,
17375         /**
17376         * @event beforeclick
17377         * Fires before click processing. Return false to cancel the default action.
17378         * @param {Node} this This node
17379         * @param {Roo.EventObject} e The event object
17380         */
17381         "beforeclick":true,
17382         /**
17383         * @event checkchange
17384         * Fires when a node with a checkbox's checked property changes
17385         * @param {Node} this This node
17386         * @param {Boolean} checked
17387         */
17388         "checkchange":true,
17389         /**
17390         * @event click
17391         * Fires when this node is clicked
17392         * @param {Node} this This node
17393         * @param {Roo.EventObject} e The event object
17394         */
17395         "click":true,
17396         /**
17397         * @event dblclick
17398         * Fires when this node is double clicked
17399         * @param {Node} this This node
17400         * @param {Roo.EventObject} e The event object
17401         */
17402         "dblclick":true,
17403         /**
17404         * @event contextmenu
17405         * Fires when this node is right clicked
17406         * @param {Node} this This node
17407         * @param {Roo.EventObject} e The event object
17408         */
17409         "contextmenu":true,
17410         /**
17411         * @event beforechildrenrendered
17412         * Fires right before the child nodes for this node are rendered
17413         * @param {Node} this This node
17414         */
17415         "beforechildrenrendered":true
17416     });
17417
17418     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17419
17420     /**
17421      * Read-only. The UI for this node
17422      * @type TreeNodeUI
17423      */
17424     this.ui = new uiClass(this);
17425     
17426     // finally support items[]
17427     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17428         return;
17429     }
17430     
17431     
17432     Roo.each(this.attributes.items, function(c) {
17433         this.appendChild(Roo.factory(c,Roo.Tree));
17434     }, this);
17435     delete this.attributes.items;
17436     
17437     
17438     
17439 };
17440 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17441     preventHScroll: true,
17442     /**
17443      * Returns true if this node is expanded
17444      * @return {Boolean}
17445      */
17446     isExpanded : function(){
17447         return this.expanded;
17448     },
17449
17450     /**
17451      * Returns the UI object for this node
17452      * @return {TreeNodeUI}
17453      */
17454     getUI : function(){
17455         return this.ui;
17456     },
17457
17458     // private override
17459     setFirstChild : function(node){
17460         var of = this.firstChild;
17461         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17462         if(this.childrenRendered && of && node != of){
17463             of.renderIndent(true, true);
17464         }
17465         if(this.rendered){
17466             this.renderIndent(true, true);
17467         }
17468     },
17469
17470     // private override
17471     setLastChild : function(node){
17472         var ol = this.lastChild;
17473         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17474         if(this.childrenRendered && ol && node != ol){
17475             ol.renderIndent(true, true);
17476         }
17477         if(this.rendered){
17478             this.renderIndent(true, true);
17479         }
17480     },
17481
17482     // these methods are overridden to provide lazy rendering support
17483     // private override
17484     appendChild : function()
17485     {
17486         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17487         if(node && this.childrenRendered){
17488             node.render();
17489         }
17490         this.ui.updateExpandIcon();
17491         return node;
17492     },
17493
17494     // private override
17495     removeChild : function(node){
17496         this.ownerTree.getSelectionModel().unselect(node);
17497         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17498         // if it's been rendered remove dom node
17499         if(this.childrenRendered){
17500             node.ui.remove();
17501         }
17502         if(this.childNodes.length < 1){
17503             this.collapse(false, false);
17504         }else{
17505             this.ui.updateExpandIcon();
17506         }
17507         if(!this.firstChild) {
17508             this.childrenRendered = false;
17509         }
17510         return node;
17511     },
17512
17513     // private override
17514     insertBefore : function(node, refNode){
17515         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17516         if(newNode && refNode && this.childrenRendered){
17517             node.render();
17518         }
17519         this.ui.updateExpandIcon();
17520         return newNode;
17521     },
17522
17523     /**
17524      * Sets the text for this node
17525      * @param {String} text
17526      */
17527     setText : function(text){
17528         var oldText = this.text;
17529         this.text = text;
17530         this.attributes.text = text;
17531         if(this.rendered){ // event without subscribing
17532             this.ui.onTextChange(this, text, oldText);
17533         }
17534         this.fireEvent("textchange", this, text, oldText);
17535     },
17536
17537     /**
17538      * Triggers selection of this node
17539      */
17540     select : function(){
17541         this.getOwnerTree().getSelectionModel().select(this);
17542     },
17543
17544     /**
17545      * Triggers deselection of this node
17546      */
17547     unselect : function(){
17548         this.getOwnerTree().getSelectionModel().unselect(this);
17549     },
17550
17551     /**
17552      * Returns true if this node is selected
17553      * @return {Boolean}
17554      */
17555     isSelected : function(){
17556         return this.getOwnerTree().getSelectionModel().isSelected(this);
17557     },
17558
17559     /**
17560      * Expand this node.
17561      * @param {Boolean} deep (optional) True to expand all children as well
17562      * @param {Boolean} anim (optional) false to cancel the default animation
17563      * @param {Function} callback (optional) A callback to be called when
17564      * expanding this node completes (does not wait for deep expand to complete).
17565      * Called with 1 parameter, this node.
17566      */
17567     expand : function(deep, anim, callback){
17568         if(!this.expanded){
17569             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17570                 return;
17571             }
17572             if(!this.childrenRendered){
17573                 this.renderChildren();
17574             }
17575             this.expanded = true;
17576             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17577                 this.ui.animExpand(function(){
17578                     this.fireEvent("expand", this);
17579                     if(typeof callback == "function"){
17580                         callback(this);
17581                     }
17582                     if(deep === true){
17583                         this.expandChildNodes(true);
17584                     }
17585                 }.createDelegate(this));
17586                 return;
17587             }else{
17588                 this.ui.expand();
17589                 this.fireEvent("expand", this);
17590                 if(typeof callback == "function"){
17591                     callback(this);
17592                 }
17593             }
17594         }else{
17595            if(typeof callback == "function"){
17596                callback(this);
17597            }
17598         }
17599         if(deep === true){
17600             this.expandChildNodes(true);
17601         }
17602     },
17603
17604     isHiddenRoot : function(){
17605         return this.isRoot && !this.getOwnerTree().rootVisible;
17606     },
17607
17608     /**
17609      * Collapse this node.
17610      * @param {Boolean} deep (optional) True to collapse all children as well
17611      * @param {Boolean} anim (optional) false to cancel the default animation
17612      */
17613     collapse : function(deep, anim){
17614         if(this.expanded && !this.isHiddenRoot()){
17615             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17616                 return;
17617             }
17618             this.expanded = false;
17619             if((this.getOwnerTree().animate && anim !== false) || anim){
17620                 this.ui.animCollapse(function(){
17621                     this.fireEvent("collapse", this);
17622                     if(deep === true){
17623                         this.collapseChildNodes(true);
17624                     }
17625                 }.createDelegate(this));
17626                 return;
17627             }else{
17628                 this.ui.collapse();
17629                 this.fireEvent("collapse", this);
17630             }
17631         }
17632         if(deep === true){
17633             var cs = this.childNodes;
17634             for(var i = 0, len = cs.length; i < len; i++) {
17635                 cs[i].collapse(true, false);
17636             }
17637         }
17638     },
17639
17640     // private
17641     delayedExpand : function(delay){
17642         if(!this.expandProcId){
17643             this.expandProcId = this.expand.defer(delay, this);
17644         }
17645     },
17646
17647     // private
17648     cancelExpand : function(){
17649         if(this.expandProcId){
17650             clearTimeout(this.expandProcId);
17651         }
17652         this.expandProcId = false;
17653     },
17654
17655     /**
17656      * Toggles expanded/collapsed state of the node
17657      */
17658     toggle : function(){
17659         if(this.expanded){
17660             this.collapse();
17661         }else{
17662             this.expand();
17663         }
17664     },
17665
17666     /**
17667      * Ensures all parent nodes are expanded
17668      */
17669     ensureVisible : function(callback){
17670         var tree = this.getOwnerTree();
17671         tree.expandPath(this.parentNode.getPath(), false, function(){
17672             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17673             Roo.callback(callback);
17674         }.createDelegate(this));
17675     },
17676
17677     /**
17678      * Expand all child nodes
17679      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17680      */
17681     expandChildNodes : function(deep){
17682         var cs = this.childNodes;
17683         for(var i = 0, len = cs.length; i < len; i++) {
17684                 cs[i].expand(deep);
17685         }
17686     },
17687
17688     /**
17689      * Collapse all child nodes
17690      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17691      */
17692     collapseChildNodes : function(deep){
17693         var cs = this.childNodes;
17694         for(var i = 0, len = cs.length; i < len; i++) {
17695                 cs[i].collapse(deep);
17696         }
17697     },
17698
17699     /**
17700      * Disables this node
17701      */
17702     disable : function(){
17703         this.disabled = true;
17704         this.unselect();
17705         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17706             this.ui.onDisableChange(this, true);
17707         }
17708         this.fireEvent("disabledchange", this, true);
17709     },
17710
17711     /**
17712      * Enables this node
17713      */
17714     enable : function(){
17715         this.disabled = false;
17716         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17717             this.ui.onDisableChange(this, false);
17718         }
17719         this.fireEvent("disabledchange", this, false);
17720     },
17721
17722     // private
17723     renderChildren : function(suppressEvent){
17724         if(suppressEvent !== false){
17725             this.fireEvent("beforechildrenrendered", this);
17726         }
17727         var cs = this.childNodes;
17728         for(var i = 0, len = cs.length; i < len; i++){
17729             cs[i].render(true);
17730         }
17731         this.childrenRendered = true;
17732     },
17733
17734     // private
17735     sort : function(fn, scope){
17736         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17737         if(this.childrenRendered){
17738             var cs = this.childNodes;
17739             for(var i = 0, len = cs.length; i < len; i++){
17740                 cs[i].render(true);
17741             }
17742         }
17743     },
17744
17745     // private
17746     render : function(bulkRender){
17747         this.ui.render(bulkRender);
17748         if(!this.rendered){
17749             this.rendered = true;
17750             if(this.expanded){
17751                 this.expanded = false;
17752                 this.expand(false, false);
17753             }
17754         }
17755     },
17756
17757     // private
17758     renderIndent : function(deep, refresh){
17759         if(refresh){
17760             this.ui.childIndent = null;
17761         }
17762         this.ui.renderIndent();
17763         if(deep === true && this.childrenRendered){
17764             var cs = this.childNodes;
17765             for(var i = 0, len = cs.length; i < len; i++){
17766                 cs[i].renderIndent(true, refresh);
17767             }
17768         }
17769     }
17770 });/*
17771  * Based on:
17772  * Ext JS Library 1.1.1
17773  * Copyright(c) 2006-2007, Ext JS, LLC.
17774  *
17775  * Originally Released Under LGPL - original licence link has changed is not relivant.
17776  *
17777  * Fork - LGPL
17778  * <script type="text/javascript">
17779  */
17780  
17781 /**
17782  * @class Roo.tree.AsyncTreeNode
17783  * @extends Roo.tree.TreeNode
17784  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17785  * @constructor
17786  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17787  */
17788  Roo.tree.AsyncTreeNode = function(config){
17789     this.loaded = false;
17790     this.loading = false;
17791     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17792     /**
17793     * @event beforeload
17794     * Fires before this node is loaded, return false to cancel
17795     * @param {Node} this This node
17796     */
17797     this.addEvents({'beforeload':true, 'load': true});
17798     /**
17799     * @event load
17800     * Fires when this node is loaded
17801     * @param {Node} this This node
17802     */
17803     /**
17804      * The loader used by this node (defaults to using the tree's defined loader)
17805      * @type TreeLoader
17806      * @property loader
17807      */
17808 };
17809 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17810     expand : function(deep, anim, callback){
17811         if(this.loading){ // if an async load is already running, waiting til it's done
17812             var timer;
17813             var f = function(){
17814                 if(!this.loading){ // done loading
17815                     clearInterval(timer);
17816                     this.expand(deep, anim, callback);
17817                 }
17818             }.createDelegate(this);
17819             timer = setInterval(f, 200);
17820             return;
17821         }
17822         if(!this.loaded){
17823             if(this.fireEvent("beforeload", this) === false){
17824                 return;
17825             }
17826             this.loading = true;
17827             this.ui.beforeLoad(this);
17828             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17829             if(loader){
17830                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17831                 return;
17832             }
17833         }
17834         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17835     },
17836     
17837     /**
17838      * Returns true if this node is currently loading
17839      * @return {Boolean}
17840      */
17841     isLoading : function(){
17842         return this.loading;  
17843     },
17844     
17845     loadComplete : function(deep, anim, callback){
17846         this.loading = false;
17847         this.loaded = true;
17848         this.ui.afterLoad(this);
17849         this.fireEvent("load", this);
17850         this.expand(deep, anim, callback);
17851     },
17852     
17853     /**
17854      * Returns true if this node has been loaded
17855      * @return {Boolean}
17856      */
17857     isLoaded : function(){
17858         return this.loaded;
17859     },
17860     
17861     hasChildNodes : function(){
17862         if(!this.isLeaf() && !this.loaded){
17863             return true;
17864         }else{
17865             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17866         }
17867     },
17868
17869     /**
17870      * Trigger a reload for this node
17871      * @param {Function} callback
17872      */
17873     reload : function(callback){
17874         this.collapse(false, false);
17875         while(this.firstChild){
17876             this.removeChild(this.firstChild);
17877         }
17878         this.childrenRendered = false;
17879         this.loaded = false;
17880         if(this.isHiddenRoot()){
17881             this.expanded = false;
17882         }
17883         this.expand(false, false, callback);
17884     }
17885 });/*
17886  * Based on:
17887  * Ext JS Library 1.1.1
17888  * Copyright(c) 2006-2007, Ext JS, LLC.
17889  *
17890  * Originally Released Under LGPL - original licence link has changed is not relivant.
17891  *
17892  * Fork - LGPL
17893  * <script type="text/javascript">
17894  */
17895  
17896 /**
17897  * @class Roo.tree.TreeNodeUI
17898  * @constructor
17899  * @param {Object} node The node to render
17900  * The TreeNode UI implementation is separate from the
17901  * tree implementation. Unless you are customizing the tree UI,
17902  * you should never have to use this directly.
17903  */
17904 Roo.tree.TreeNodeUI = function(node){
17905     this.node = node;
17906     this.rendered = false;
17907     this.animating = false;
17908     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17909 };
17910
17911 Roo.tree.TreeNodeUI.prototype = {
17912     removeChild : function(node){
17913         if(this.rendered){
17914             this.ctNode.removeChild(node.ui.getEl());
17915         }
17916     },
17917
17918     beforeLoad : function(){
17919          this.addClass("x-tree-node-loading");
17920     },
17921
17922     afterLoad : function(){
17923          this.removeClass("x-tree-node-loading");
17924     },
17925
17926     onTextChange : function(node, text, oldText){
17927         if(this.rendered){
17928             this.textNode.innerHTML = text;
17929         }
17930     },
17931
17932     onDisableChange : function(node, state){
17933         this.disabled = state;
17934         if(state){
17935             this.addClass("x-tree-node-disabled");
17936         }else{
17937             this.removeClass("x-tree-node-disabled");
17938         }
17939     },
17940
17941     onSelectedChange : function(state){
17942         if(state){
17943             this.focus();
17944             this.addClass("x-tree-selected");
17945         }else{
17946             //this.blur();
17947             this.removeClass("x-tree-selected");
17948         }
17949     },
17950
17951     onMove : function(tree, node, oldParent, newParent, index, refNode){
17952         this.childIndent = null;
17953         if(this.rendered){
17954             var targetNode = newParent.ui.getContainer();
17955             if(!targetNode){//target not rendered
17956                 this.holder = document.createElement("div");
17957                 this.holder.appendChild(this.wrap);
17958                 return;
17959             }
17960             var insertBefore = refNode ? refNode.ui.getEl() : null;
17961             if(insertBefore){
17962                 targetNode.insertBefore(this.wrap, insertBefore);
17963             }else{
17964                 targetNode.appendChild(this.wrap);
17965             }
17966             this.node.renderIndent(true);
17967         }
17968     },
17969
17970     addClass : function(cls){
17971         if(this.elNode){
17972             Roo.fly(this.elNode).addClass(cls);
17973         }
17974     },
17975
17976     removeClass : function(cls){
17977         if(this.elNode){
17978             Roo.fly(this.elNode).removeClass(cls);
17979         }
17980     },
17981
17982     remove : function(){
17983         if(this.rendered){
17984             this.holder = document.createElement("div");
17985             this.holder.appendChild(this.wrap);
17986         }
17987     },
17988
17989     fireEvent : function(){
17990         return this.node.fireEvent.apply(this.node, arguments);
17991     },
17992
17993     initEvents : function(){
17994         this.node.on("move", this.onMove, this);
17995         var E = Roo.EventManager;
17996         var a = this.anchor;
17997
17998         var el = Roo.fly(a, '_treeui');
17999
18000         if(Roo.isOpera){ // opera render bug ignores the CSS
18001             el.setStyle("text-decoration", "none");
18002         }
18003
18004         el.on("click", this.onClick, this);
18005         el.on("dblclick", this.onDblClick, this);
18006
18007         if(this.checkbox){
18008             Roo.EventManager.on(this.checkbox,
18009                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18010         }
18011
18012         el.on("contextmenu", this.onContextMenu, this);
18013
18014         var icon = Roo.fly(this.iconNode);
18015         icon.on("click", this.onClick, this);
18016         icon.on("dblclick", this.onDblClick, this);
18017         icon.on("contextmenu", this.onContextMenu, this);
18018         E.on(this.ecNode, "click", this.ecClick, this, true);
18019
18020         if(this.node.disabled){
18021             this.addClass("x-tree-node-disabled");
18022         }
18023         if(this.node.hidden){
18024             this.addClass("x-tree-node-disabled");
18025         }
18026         var ot = this.node.getOwnerTree();
18027         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18028         if(dd && (!this.node.isRoot || ot.rootVisible)){
18029             Roo.dd.Registry.register(this.elNode, {
18030                 node: this.node,
18031                 handles: this.getDDHandles(),
18032                 isHandle: false
18033             });
18034         }
18035     },
18036
18037     getDDHandles : function(){
18038         return [this.iconNode, this.textNode];
18039     },
18040
18041     hide : function(){
18042         if(this.rendered){
18043             this.wrap.style.display = "none";
18044         }
18045     },
18046
18047     show : function(){
18048         if(this.rendered){
18049             this.wrap.style.display = "";
18050         }
18051     },
18052
18053     onContextMenu : function(e){
18054         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18055             e.preventDefault();
18056             this.focus();
18057             this.fireEvent("contextmenu", this.node, e);
18058         }
18059     },
18060
18061     onClick : function(e){
18062         if(this.dropping){
18063             e.stopEvent();
18064             return;
18065         }
18066         if(this.fireEvent("beforeclick", this.node, e) !== false){
18067             if(!this.disabled && this.node.attributes.href){
18068                 this.fireEvent("click", this.node, e);
18069                 return;
18070             }
18071             e.preventDefault();
18072             if(this.disabled){
18073                 return;
18074             }
18075
18076             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18077                 this.node.toggle();
18078             }
18079
18080             this.fireEvent("click", this.node, e);
18081         }else{
18082             e.stopEvent();
18083         }
18084     },
18085
18086     onDblClick : function(e){
18087         e.preventDefault();
18088         if(this.disabled){
18089             return;
18090         }
18091         if(this.checkbox){
18092             this.toggleCheck();
18093         }
18094         if(!this.animating && this.node.hasChildNodes()){
18095             this.node.toggle();
18096         }
18097         this.fireEvent("dblclick", this.node, e);
18098     },
18099
18100     onCheckChange : function(){
18101         var checked = this.checkbox.checked;
18102         this.node.attributes.checked = checked;
18103         this.fireEvent('checkchange', this.node, checked);
18104     },
18105
18106     ecClick : function(e){
18107         if(!this.animating && this.node.hasChildNodes()){
18108             this.node.toggle();
18109         }
18110     },
18111
18112     startDrop : function(){
18113         this.dropping = true;
18114     },
18115
18116     // delayed drop so the click event doesn't get fired on a drop
18117     endDrop : function(){
18118        setTimeout(function(){
18119            this.dropping = false;
18120        }.createDelegate(this), 50);
18121     },
18122
18123     expand : function(){
18124         this.updateExpandIcon();
18125         this.ctNode.style.display = "";
18126     },
18127
18128     focus : function(){
18129         if(!this.node.preventHScroll){
18130             try{this.anchor.focus();
18131             }catch(e){}
18132         }else if(!Roo.isIE){
18133             try{
18134                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18135                 var l = noscroll.scrollLeft;
18136                 this.anchor.focus();
18137                 noscroll.scrollLeft = l;
18138             }catch(e){}
18139         }
18140     },
18141
18142     toggleCheck : function(value){
18143         var cb = this.checkbox;
18144         if(cb){
18145             cb.checked = (value === undefined ? !cb.checked : value);
18146         }
18147     },
18148
18149     blur : function(){
18150         try{
18151             this.anchor.blur();
18152         }catch(e){}
18153     },
18154
18155     animExpand : function(callback){
18156         var ct = Roo.get(this.ctNode);
18157         ct.stopFx();
18158         if(!this.node.hasChildNodes()){
18159             this.updateExpandIcon();
18160             this.ctNode.style.display = "";
18161             Roo.callback(callback);
18162             return;
18163         }
18164         this.animating = true;
18165         this.updateExpandIcon();
18166
18167         ct.slideIn('t', {
18168            callback : function(){
18169                this.animating = false;
18170                Roo.callback(callback);
18171             },
18172             scope: this,
18173             duration: this.node.ownerTree.duration || .25
18174         });
18175     },
18176
18177     highlight : function(){
18178         var tree = this.node.getOwnerTree();
18179         Roo.fly(this.wrap).highlight(
18180             tree.hlColor || "C3DAF9",
18181             {endColor: tree.hlBaseColor}
18182         );
18183     },
18184
18185     collapse : function(){
18186         this.updateExpandIcon();
18187         this.ctNode.style.display = "none";
18188     },
18189
18190     animCollapse : function(callback){
18191         var ct = Roo.get(this.ctNode);
18192         ct.enableDisplayMode('block');
18193         ct.stopFx();
18194
18195         this.animating = true;
18196         this.updateExpandIcon();
18197
18198         ct.slideOut('t', {
18199             callback : function(){
18200                this.animating = false;
18201                Roo.callback(callback);
18202             },
18203             scope: this,
18204             duration: this.node.ownerTree.duration || .25
18205         });
18206     },
18207
18208     getContainer : function(){
18209         return this.ctNode;
18210     },
18211
18212     getEl : function(){
18213         return this.wrap;
18214     },
18215
18216     appendDDGhost : function(ghostNode){
18217         ghostNode.appendChild(this.elNode.cloneNode(true));
18218     },
18219
18220     getDDRepairXY : function(){
18221         return Roo.lib.Dom.getXY(this.iconNode);
18222     },
18223
18224     onRender : function(){
18225         this.render();
18226     },
18227
18228     render : function(bulkRender){
18229         var n = this.node, a = n.attributes;
18230         var targetNode = n.parentNode ?
18231               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18232
18233         if(!this.rendered){
18234             this.rendered = true;
18235
18236             this.renderElements(n, a, targetNode, bulkRender);
18237
18238             if(a.qtip){
18239                if(this.textNode.setAttributeNS){
18240                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18241                    if(a.qtipTitle){
18242                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18243                    }
18244                }else{
18245                    this.textNode.setAttribute("ext:qtip", a.qtip);
18246                    if(a.qtipTitle){
18247                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18248                    }
18249                }
18250             }else if(a.qtipCfg){
18251                 a.qtipCfg.target = Roo.id(this.textNode);
18252                 Roo.QuickTips.register(a.qtipCfg);
18253             }
18254             this.initEvents();
18255             if(!this.node.expanded){
18256                 this.updateExpandIcon();
18257             }
18258         }else{
18259             if(bulkRender === true) {
18260                 targetNode.appendChild(this.wrap);
18261             }
18262         }
18263     },
18264
18265     renderElements : function(n, a, targetNode, bulkRender)
18266     {
18267         // add some indent caching, this helps performance when rendering a large tree
18268         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18269         var t = n.getOwnerTree();
18270         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18271         if (typeof(n.attributes.html) != 'undefined') {
18272             txt = n.attributes.html;
18273         }
18274         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18275         var cb = typeof a.checked == 'boolean';
18276         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18277         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18278             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18279             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18280             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18281             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18282             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18283              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18284                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18285             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18286             "</li>"];
18287
18288         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18289             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18290                                 n.nextSibling.ui.getEl(), buf.join(""));
18291         }else{
18292             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18293         }
18294
18295         this.elNode = this.wrap.childNodes[0];
18296         this.ctNode = this.wrap.childNodes[1];
18297         var cs = this.elNode.childNodes;
18298         this.indentNode = cs[0];
18299         this.ecNode = cs[1];
18300         this.iconNode = cs[2];
18301         var index = 3;
18302         if(cb){
18303             this.checkbox = cs[3];
18304             index++;
18305         }
18306         this.anchor = cs[index];
18307         this.textNode = cs[index].firstChild;
18308     },
18309
18310     getAnchor : function(){
18311         return this.anchor;
18312     },
18313
18314     getTextEl : function(){
18315         return this.textNode;
18316     },
18317
18318     getIconEl : function(){
18319         return this.iconNode;
18320     },
18321
18322     isChecked : function(){
18323         return this.checkbox ? this.checkbox.checked : false;
18324     },
18325
18326     updateExpandIcon : function(){
18327         if(this.rendered){
18328             var n = this.node, c1, c2;
18329             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18330             var hasChild = n.hasChildNodes();
18331             if(hasChild){
18332                 if(n.expanded){
18333                     cls += "-minus";
18334                     c1 = "x-tree-node-collapsed";
18335                     c2 = "x-tree-node-expanded";
18336                 }else{
18337                     cls += "-plus";
18338                     c1 = "x-tree-node-expanded";
18339                     c2 = "x-tree-node-collapsed";
18340                 }
18341                 if(this.wasLeaf){
18342                     this.removeClass("x-tree-node-leaf");
18343                     this.wasLeaf = false;
18344                 }
18345                 if(this.c1 != c1 || this.c2 != c2){
18346                     Roo.fly(this.elNode).replaceClass(c1, c2);
18347                     this.c1 = c1; this.c2 = c2;
18348                 }
18349             }else{
18350                 // this changes non-leafs into leafs if they have no children.
18351                 // it's not very rational behaviour..
18352                 
18353                 if(!this.wasLeaf && this.node.leaf){
18354                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18355                     delete this.c1;
18356                     delete this.c2;
18357                     this.wasLeaf = true;
18358                 }
18359             }
18360             var ecc = "x-tree-ec-icon "+cls;
18361             if(this.ecc != ecc){
18362                 this.ecNode.className = ecc;
18363                 this.ecc = ecc;
18364             }
18365         }
18366     },
18367
18368     getChildIndent : function(){
18369         if(!this.childIndent){
18370             var buf = [];
18371             var p = this.node;
18372             while(p){
18373                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18374                     if(!p.isLast()) {
18375                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18376                     } else {
18377                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18378                     }
18379                 }
18380                 p = p.parentNode;
18381             }
18382             this.childIndent = buf.join("");
18383         }
18384         return this.childIndent;
18385     },
18386
18387     renderIndent : function(){
18388         if(this.rendered){
18389             var indent = "";
18390             var p = this.node.parentNode;
18391             if(p){
18392                 indent = p.ui.getChildIndent();
18393             }
18394             if(this.indentMarkup != indent){ // don't rerender if not required
18395                 this.indentNode.innerHTML = indent;
18396                 this.indentMarkup = indent;
18397             }
18398             this.updateExpandIcon();
18399         }
18400     }
18401 };
18402
18403 Roo.tree.RootTreeNodeUI = function(){
18404     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18405 };
18406 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18407     render : function(){
18408         if(!this.rendered){
18409             var targetNode = this.node.ownerTree.innerCt.dom;
18410             this.node.expanded = true;
18411             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18412             this.wrap = this.ctNode = targetNode.firstChild;
18413         }
18414     },
18415     collapse : function(){
18416     },
18417     expand : function(){
18418     }
18419 });/*
18420  * Based on:
18421  * Ext JS Library 1.1.1
18422  * Copyright(c) 2006-2007, Ext JS, LLC.
18423  *
18424  * Originally Released Under LGPL - original licence link has changed is not relivant.
18425  *
18426  * Fork - LGPL
18427  * <script type="text/javascript">
18428  */
18429 /**
18430  * @class Roo.tree.TreeLoader
18431  * @extends Roo.util.Observable
18432  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18433  * nodes from a specified URL. The response must be a javascript Array definition
18434  * who's elements are node definition objects. eg:
18435  * <pre><code>
18436 {  success : true,
18437    data :      [
18438    
18439     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18440     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18441     ]
18442 }
18443
18444
18445 </code></pre>
18446  * <br><br>
18447  * The old style respose with just an array is still supported, but not recommended.
18448  * <br><br>
18449  *
18450  * A server request is sent, and child nodes are loaded only when a node is expanded.
18451  * The loading node's id is passed to the server under the parameter name "node" to
18452  * enable the server to produce the correct child nodes.
18453  * <br><br>
18454  * To pass extra parameters, an event handler may be attached to the "beforeload"
18455  * event, and the parameters specified in the TreeLoader's baseParams property:
18456  * <pre><code>
18457     myTreeLoader.on("beforeload", function(treeLoader, node) {
18458         this.baseParams.category = node.attributes.category;
18459     }, this);
18460 </code></pre><
18461  * This would pass an HTTP parameter called "category" to the server containing
18462  * the value of the Node's "category" attribute.
18463  * @constructor
18464  * Creates a new Treeloader.
18465  * @param {Object} config A config object containing config properties.
18466  */
18467 Roo.tree.TreeLoader = function(config){
18468     this.baseParams = {};
18469     this.requestMethod = "POST";
18470     Roo.apply(this, config);
18471
18472     this.addEvents({
18473     
18474         /**
18475          * @event beforeload
18476          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18477          * @param {Object} This TreeLoader object.
18478          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18479          * @param {Object} callback The callback function specified in the {@link #load} call.
18480          */
18481         beforeload : true,
18482         /**
18483          * @event load
18484          * Fires when the node has been successfuly loaded.
18485          * @param {Object} This TreeLoader object.
18486          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18487          * @param {Object} response The response object containing the data from the server.
18488          */
18489         load : true,
18490         /**
18491          * @event loadexception
18492          * Fires if the network request failed.
18493          * @param {Object} This TreeLoader object.
18494          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18495          * @param {Object} response The response object containing the data from the server.
18496          */
18497         loadexception : true,
18498         /**
18499          * @event create
18500          * Fires before a node is created, enabling you to return custom Node types 
18501          * @param {Object} This TreeLoader object.
18502          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18503          */
18504         create : true
18505     });
18506
18507     Roo.tree.TreeLoader.superclass.constructor.call(this);
18508 };
18509
18510 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18511     /**
18512     * @cfg {String} dataUrl The URL from which to request a Json string which
18513     * specifies an array of node definition object representing the child nodes
18514     * to be loaded.
18515     */
18516     /**
18517     * @cfg {String} requestMethod either GET or POST
18518     * defaults to POST (due to BC)
18519     * to be loaded.
18520     */
18521     /**
18522     * @cfg {Object} baseParams (optional) An object containing properties which
18523     * specify HTTP parameters to be passed to each request for child nodes.
18524     */
18525     /**
18526     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18527     * created by this loader. If the attributes sent by the server have an attribute in this object,
18528     * they take priority.
18529     */
18530     /**
18531     * @cfg {Object} uiProviders (optional) An object containing properties which
18532     * 
18533     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18534     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18535     * <i>uiProvider</i> attribute of a returned child node is a string rather
18536     * than a reference to a TreeNodeUI implementation, this that string value
18537     * is used as a property name in the uiProviders object. You can define the provider named
18538     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18539     */
18540     uiProviders : {},
18541
18542     /**
18543     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18544     * child nodes before loading.
18545     */
18546     clearOnLoad : true,
18547
18548     /**
18549     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18550     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18551     * Grid query { data : [ .....] }
18552     */
18553     
18554     root : false,
18555      /**
18556     * @cfg {String} queryParam (optional) 
18557     * Name of the query as it will be passed on the querystring (defaults to 'node')
18558     * eg. the request will be ?node=[id]
18559     */
18560     
18561     
18562     queryParam: false,
18563     
18564     /**
18565      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18566      * This is called automatically when a node is expanded, but may be used to reload
18567      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18568      * @param {Roo.tree.TreeNode} node
18569      * @param {Function} callback
18570      */
18571     load : function(node, callback){
18572         if(this.clearOnLoad){
18573             while(node.firstChild){
18574                 node.removeChild(node.firstChild);
18575             }
18576         }
18577         if(node.attributes.children){ // preloaded json children
18578             var cs = node.attributes.children;
18579             for(var i = 0, len = cs.length; i < len; i++){
18580                 node.appendChild(this.createNode(cs[i]));
18581             }
18582             if(typeof callback == "function"){
18583                 callback();
18584             }
18585         }else if(this.dataUrl){
18586             this.requestData(node, callback);
18587         }
18588     },
18589
18590     getParams: function(node){
18591         var buf = [], bp = this.baseParams;
18592         for(var key in bp){
18593             if(typeof bp[key] != "function"){
18594                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18595             }
18596         }
18597         var n = this.queryParam === false ? 'node' : this.queryParam;
18598         buf.push(n + "=", encodeURIComponent(node.id));
18599         return buf.join("");
18600     },
18601
18602     requestData : function(node, callback){
18603         if(this.fireEvent("beforeload", this, node, callback) !== false){
18604             this.transId = Roo.Ajax.request({
18605                 method:this.requestMethod,
18606                 url: this.dataUrl||this.url,
18607                 success: this.handleResponse,
18608                 failure: this.handleFailure,
18609                 scope: this,
18610                 argument: {callback: callback, node: node},
18611                 params: this.getParams(node)
18612             });
18613         }else{
18614             // if the load is cancelled, make sure we notify
18615             // the node that we are done
18616             if(typeof callback == "function"){
18617                 callback();
18618             }
18619         }
18620     },
18621
18622     isLoading : function(){
18623         return this.transId ? true : false;
18624     },
18625
18626     abort : function(){
18627         if(this.isLoading()){
18628             Roo.Ajax.abort(this.transId);
18629         }
18630     },
18631
18632     // private
18633     createNode : function(attr)
18634     {
18635         // apply baseAttrs, nice idea Corey!
18636         if(this.baseAttrs){
18637             Roo.applyIf(attr, this.baseAttrs);
18638         }
18639         if(this.applyLoader !== false){
18640             attr.loader = this;
18641         }
18642         // uiProvider = depreciated..
18643         
18644         if(typeof(attr.uiProvider) == 'string'){
18645            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18646                 /**  eval:var:attr */ eval(attr.uiProvider);
18647         }
18648         if(typeof(this.uiProviders['default']) != 'undefined') {
18649             attr.uiProvider = this.uiProviders['default'];
18650         }
18651         
18652         this.fireEvent('create', this, attr);
18653         
18654         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18655         return(attr.leaf ?
18656                         new Roo.tree.TreeNode(attr) :
18657                         new Roo.tree.AsyncTreeNode(attr));
18658     },
18659
18660     processResponse : function(response, node, callback)
18661     {
18662         var json = response.responseText;
18663         try {
18664             
18665             var o = Roo.decode(json);
18666             
18667             if (this.root === false && typeof(o.success) != undefined) {
18668                 this.root = 'data'; // the default behaviour for list like data..
18669                 }
18670                 
18671             if (this.root !== false &&  !o.success) {
18672                 // it's a failure condition.
18673                 var a = response.argument;
18674                 this.fireEvent("loadexception", this, a.node, response);
18675                 Roo.log("Load failed - should have a handler really");
18676                 return;
18677             }
18678             
18679             
18680             
18681             if (this.root !== false) {
18682                  o = o[this.root];
18683             }
18684             
18685             for(var i = 0, len = o.length; i < len; i++){
18686                 var n = this.createNode(o[i]);
18687                 if(n){
18688                     node.appendChild(n);
18689                 }
18690             }
18691             if(typeof callback == "function"){
18692                 callback(this, node);
18693             }
18694         }catch(e){
18695             this.handleFailure(response);
18696         }
18697     },
18698
18699     handleResponse : function(response){
18700         this.transId = false;
18701         var a = response.argument;
18702         this.processResponse(response, a.node, a.callback);
18703         this.fireEvent("load", this, a.node, response);
18704     },
18705
18706     handleFailure : function(response)
18707     {
18708         // should handle failure better..
18709         this.transId = false;
18710         var a = response.argument;
18711         this.fireEvent("loadexception", this, a.node, response);
18712         if(typeof a.callback == "function"){
18713             a.callback(this, a.node);
18714         }
18715     }
18716 });/*
18717  * Based on:
18718  * Ext JS Library 1.1.1
18719  * Copyright(c) 2006-2007, Ext JS, LLC.
18720  *
18721  * Originally Released Under LGPL - original licence link has changed is not relivant.
18722  *
18723  * Fork - LGPL
18724  * <script type="text/javascript">
18725  */
18726
18727 /**
18728 * @class Roo.tree.TreeFilter
18729 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18730 * @param {TreePanel} tree
18731 * @param {Object} config (optional)
18732  */
18733 Roo.tree.TreeFilter = function(tree, config){
18734     this.tree = tree;
18735     this.filtered = {};
18736     Roo.apply(this, config);
18737 };
18738
18739 Roo.tree.TreeFilter.prototype = {
18740     clearBlank:false,
18741     reverse:false,
18742     autoClear:false,
18743     remove:false,
18744
18745      /**
18746      * Filter the data by a specific attribute.
18747      * @param {String/RegExp} value Either string that the attribute value
18748      * should start with or a RegExp to test against the attribute
18749      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18750      * @param {TreeNode} startNode (optional) The node to start the filter at.
18751      */
18752     filter : function(value, attr, startNode){
18753         attr = attr || "text";
18754         var f;
18755         if(typeof value == "string"){
18756             var vlen = value.length;
18757             // auto clear empty filter
18758             if(vlen == 0 && this.clearBlank){
18759                 this.clear();
18760                 return;
18761             }
18762             value = value.toLowerCase();
18763             f = function(n){
18764                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18765             };
18766         }else if(value.exec){ // regex?
18767             f = function(n){
18768                 return value.test(n.attributes[attr]);
18769             };
18770         }else{
18771             throw 'Illegal filter type, must be string or regex';
18772         }
18773         this.filterBy(f, null, startNode);
18774         },
18775
18776     /**
18777      * Filter by a function. The passed function will be called with each
18778      * node in the tree (or from the startNode). If the function returns true, the node is kept
18779      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18780      * @param {Function} fn The filter function
18781      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18782      */
18783     filterBy : function(fn, scope, startNode){
18784         startNode = startNode || this.tree.root;
18785         if(this.autoClear){
18786             this.clear();
18787         }
18788         var af = this.filtered, rv = this.reverse;
18789         var f = function(n){
18790             if(n == startNode){
18791                 return true;
18792             }
18793             if(af[n.id]){
18794                 return false;
18795             }
18796             var m = fn.call(scope || n, n);
18797             if(!m || rv){
18798                 af[n.id] = n;
18799                 n.ui.hide();
18800                 return false;
18801             }
18802             return true;
18803         };
18804         startNode.cascade(f);
18805         if(this.remove){
18806            for(var id in af){
18807                if(typeof id != "function"){
18808                    var n = af[id];
18809                    if(n && n.parentNode){
18810                        n.parentNode.removeChild(n);
18811                    }
18812                }
18813            }
18814         }
18815     },
18816
18817     /**
18818      * Clears the current filter. Note: with the "remove" option
18819      * set a filter cannot be cleared.
18820      */
18821     clear : function(){
18822         var t = this.tree;
18823         var af = this.filtered;
18824         for(var id in af){
18825             if(typeof id != "function"){
18826                 var n = af[id];
18827                 if(n){
18828                     n.ui.show();
18829                 }
18830             }
18831         }
18832         this.filtered = {};
18833     }
18834 };
18835 /*
18836  * Based on:
18837  * Ext JS Library 1.1.1
18838  * Copyright(c) 2006-2007, Ext JS, LLC.
18839  *
18840  * Originally Released Under LGPL - original licence link has changed is not relivant.
18841  *
18842  * Fork - LGPL
18843  * <script type="text/javascript">
18844  */
18845  
18846
18847 /**
18848  * @class Roo.tree.TreeSorter
18849  * Provides sorting of nodes in a TreePanel
18850  * 
18851  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18852  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18853  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18854  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18855  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18856  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18857  * @constructor
18858  * @param {TreePanel} tree
18859  * @param {Object} config
18860  */
18861 Roo.tree.TreeSorter = function(tree, config){
18862     Roo.apply(this, config);
18863     tree.on("beforechildrenrendered", this.doSort, this);
18864     tree.on("append", this.updateSort, this);
18865     tree.on("insert", this.updateSort, this);
18866     
18867     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18868     var p = this.property || "text";
18869     var sortType = this.sortType;
18870     var fs = this.folderSort;
18871     var cs = this.caseSensitive === true;
18872     var leafAttr = this.leafAttr || 'leaf';
18873
18874     this.sortFn = function(n1, n2){
18875         if(fs){
18876             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18877                 return 1;
18878             }
18879             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18880                 return -1;
18881             }
18882         }
18883         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18884         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18885         if(v1 < v2){
18886                         return dsc ? +1 : -1;
18887                 }else if(v1 > v2){
18888                         return dsc ? -1 : +1;
18889         }else{
18890                 return 0;
18891         }
18892     };
18893 };
18894
18895 Roo.tree.TreeSorter.prototype = {
18896     doSort : function(node){
18897         node.sort(this.sortFn);
18898     },
18899     
18900     compareNodes : function(n1, n2){
18901         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18902     },
18903     
18904     updateSort : function(tree, node){
18905         if(node.childrenRendered){
18906             this.doSort.defer(1, this, [node]);
18907         }
18908     }
18909 };/*
18910  * Based on:
18911  * Ext JS Library 1.1.1
18912  * Copyright(c) 2006-2007, Ext JS, LLC.
18913  *
18914  * Originally Released Under LGPL - original licence link has changed is not relivant.
18915  *
18916  * Fork - LGPL
18917  * <script type="text/javascript">
18918  */
18919
18920 if(Roo.dd.DropZone){
18921     
18922 Roo.tree.TreeDropZone = function(tree, config){
18923     this.allowParentInsert = false;
18924     this.allowContainerDrop = false;
18925     this.appendOnly = false;
18926     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18927     this.tree = tree;
18928     this.lastInsertClass = "x-tree-no-status";
18929     this.dragOverData = {};
18930 };
18931
18932 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18933     ddGroup : "TreeDD",
18934     
18935     expandDelay : 1000,
18936     
18937     expandNode : function(node){
18938         if(node.hasChildNodes() && !node.isExpanded()){
18939             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18940         }
18941     },
18942     
18943     queueExpand : function(node){
18944         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18945     },
18946     
18947     cancelExpand : function(){
18948         if(this.expandProcId){
18949             clearTimeout(this.expandProcId);
18950             this.expandProcId = false;
18951         }
18952     },
18953     
18954     isValidDropPoint : function(n, pt, dd, e, data){
18955         if(!n || !data){ return false; }
18956         var targetNode = n.node;
18957         var dropNode = data.node;
18958         // default drop rules
18959         if(!(targetNode && targetNode.isTarget && pt)){
18960             return false;
18961         }
18962         if(pt == "append" && targetNode.allowChildren === false){
18963             return false;
18964         }
18965         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18966             return false;
18967         }
18968         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18969             return false;
18970         }
18971         // reuse the object
18972         var overEvent = this.dragOverData;
18973         overEvent.tree = this.tree;
18974         overEvent.target = targetNode;
18975         overEvent.data = data;
18976         overEvent.point = pt;
18977         overEvent.source = dd;
18978         overEvent.rawEvent = e;
18979         overEvent.dropNode = dropNode;
18980         overEvent.cancel = false;  
18981         var result = this.tree.fireEvent("nodedragover", overEvent);
18982         return overEvent.cancel === false && result !== false;
18983     },
18984     
18985     getDropPoint : function(e, n, dd){
18986         var tn = n.node;
18987         if(tn.isRoot){
18988             return tn.allowChildren !== false ? "append" : false; // always append for root
18989         }
18990         var dragEl = n.ddel;
18991         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18992         var y = Roo.lib.Event.getPageY(e);
18993         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18994         
18995         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18996         var noAppend = tn.allowChildren === false;
18997         if(this.appendOnly || tn.parentNode.allowChildren === false){
18998             return noAppend ? false : "append";
18999         }
19000         var noBelow = false;
19001         if(!this.allowParentInsert){
19002             noBelow = tn.hasChildNodes() && tn.isExpanded();
19003         }
19004         var q = (b - t) / (noAppend ? 2 : 3);
19005         if(y >= t && y < (t + q)){
19006             return "above";
19007         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19008             return "below";
19009         }else{
19010             return "append";
19011         }
19012     },
19013     
19014     onNodeEnter : function(n, dd, e, data){
19015         this.cancelExpand();
19016     },
19017     
19018     onNodeOver : function(n, dd, e, data){
19019         var pt = this.getDropPoint(e, n, dd);
19020         var node = n.node;
19021         
19022         // auto node expand check
19023         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19024             this.queueExpand(node);
19025         }else if(pt != "append"){
19026             this.cancelExpand();
19027         }
19028         
19029         // set the insert point style on the target node
19030         var returnCls = this.dropNotAllowed;
19031         if(this.isValidDropPoint(n, pt, dd, e, data)){
19032            if(pt){
19033                var el = n.ddel;
19034                var cls;
19035                if(pt == "above"){
19036                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19037                    cls = "x-tree-drag-insert-above";
19038                }else if(pt == "below"){
19039                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19040                    cls = "x-tree-drag-insert-below";
19041                }else{
19042                    returnCls = "x-tree-drop-ok-append";
19043                    cls = "x-tree-drag-append";
19044                }
19045                if(this.lastInsertClass != cls){
19046                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19047                    this.lastInsertClass = cls;
19048                }
19049            }
19050        }
19051        return returnCls;
19052     },
19053     
19054     onNodeOut : function(n, dd, e, data){
19055         this.cancelExpand();
19056         this.removeDropIndicators(n);
19057     },
19058     
19059     onNodeDrop : function(n, dd, e, data){
19060         var point = this.getDropPoint(e, n, dd);
19061         var targetNode = n.node;
19062         targetNode.ui.startDrop();
19063         if(!this.isValidDropPoint(n, point, dd, e, data)){
19064             targetNode.ui.endDrop();
19065             return false;
19066         }
19067         // first try to find the drop node
19068         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19069         var dropEvent = {
19070             tree : this.tree,
19071             target: targetNode,
19072             data: data,
19073             point: point,
19074             source: dd,
19075             rawEvent: e,
19076             dropNode: dropNode,
19077             cancel: !dropNode   
19078         };
19079         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19080         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19081             targetNode.ui.endDrop();
19082             return false;
19083         }
19084         // allow target changing
19085         targetNode = dropEvent.target;
19086         if(point == "append" && !targetNode.isExpanded()){
19087             targetNode.expand(false, null, function(){
19088                 this.completeDrop(dropEvent);
19089             }.createDelegate(this));
19090         }else{
19091             this.completeDrop(dropEvent);
19092         }
19093         return true;
19094     },
19095     
19096     completeDrop : function(de){
19097         var ns = de.dropNode, p = de.point, t = de.target;
19098         if(!(ns instanceof Array)){
19099             ns = [ns];
19100         }
19101         var n;
19102         for(var i = 0, len = ns.length; i < len; i++){
19103             n = ns[i];
19104             if(p == "above"){
19105                 t.parentNode.insertBefore(n, t);
19106             }else if(p == "below"){
19107                 t.parentNode.insertBefore(n, t.nextSibling);
19108             }else{
19109                 t.appendChild(n);
19110             }
19111         }
19112         n.ui.focus();
19113         if(this.tree.hlDrop){
19114             n.ui.highlight();
19115         }
19116         t.ui.endDrop();
19117         this.tree.fireEvent("nodedrop", de);
19118     },
19119     
19120     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19121         if(this.tree.hlDrop){
19122             dropNode.ui.focus();
19123             dropNode.ui.highlight();
19124         }
19125         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19126     },
19127     
19128     getTree : function(){
19129         return this.tree;
19130     },
19131     
19132     removeDropIndicators : function(n){
19133         if(n && n.ddel){
19134             var el = n.ddel;
19135             Roo.fly(el).removeClass([
19136                     "x-tree-drag-insert-above",
19137                     "x-tree-drag-insert-below",
19138                     "x-tree-drag-append"]);
19139             this.lastInsertClass = "_noclass";
19140         }
19141     },
19142     
19143     beforeDragDrop : function(target, e, id){
19144         this.cancelExpand();
19145         return true;
19146     },
19147     
19148     afterRepair : function(data){
19149         if(data && Roo.enableFx){
19150             data.node.ui.highlight();
19151         }
19152         this.hideProxy();
19153     }    
19154 });
19155
19156 }
19157 /*
19158  * Based on:
19159  * Ext JS Library 1.1.1
19160  * Copyright(c) 2006-2007, Ext JS, LLC.
19161  *
19162  * Originally Released Under LGPL - original licence link has changed is not relivant.
19163  *
19164  * Fork - LGPL
19165  * <script type="text/javascript">
19166  */
19167  
19168
19169 if(Roo.dd.DragZone){
19170 Roo.tree.TreeDragZone = function(tree, config){
19171     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19172     this.tree = tree;
19173 };
19174
19175 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19176     ddGroup : "TreeDD",
19177     
19178     onBeforeDrag : function(data, e){
19179         var n = data.node;
19180         return n && n.draggable && !n.disabled;
19181     },
19182     
19183     onInitDrag : function(e){
19184         var data = this.dragData;
19185         this.tree.getSelectionModel().select(data.node);
19186         this.proxy.update("");
19187         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19188         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19189     },
19190     
19191     getRepairXY : function(e, data){
19192         return data.node.ui.getDDRepairXY();
19193     },
19194     
19195     onEndDrag : function(data, e){
19196         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19197     },
19198     
19199     onValidDrop : function(dd, e, id){
19200         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19201         this.hideProxy();
19202     },
19203     
19204     beforeInvalidDrop : function(e, id){
19205         // this scrolls the original position back into view
19206         var sm = this.tree.getSelectionModel();
19207         sm.clearSelections();
19208         sm.select(this.dragData.node);
19209     }
19210 });
19211 }/*
19212  * Based on:
19213  * Ext JS Library 1.1.1
19214  * Copyright(c) 2006-2007, Ext JS, LLC.
19215  *
19216  * Originally Released Under LGPL - original licence link has changed is not relivant.
19217  *
19218  * Fork - LGPL
19219  * <script type="text/javascript">
19220  */
19221 /**
19222  * @class Roo.tree.TreeEditor
19223  * @extends Roo.Editor
19224  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19225  * as the editor field.
19226  * @constructor
19227  * @param {Object} config (used to be the tree panel.)
19228  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19229  * 
19230  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19231  * @cfg {Roo.form.TextField|Object} field The field configuration
19232  *
19233  * 
19234  */
19235 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19236     var tree = config;
19237     var field;
19238     if (oldconfig) { // old style..
19239         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19240     } else {
19241         // new style..
19242         tree = config.tree;
19243         config.field = config.field  || {};
19244         config.field.xtype = 'TextField';
19245         field = Roo.factory(config.field, Roo.form);
19246     }
19247     config = config || {};
19248     
19249     
19250     this.addEvents({
19251         /**
19252          * @event beforenodeedit
19253          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19254          * false from the handler of this event.
19255          * @param {Editor} this
19256          * @param {Roo.tree.Node} node 
19257          */
19258         "beforenodeedit" : true
19259     });
19260     
19261     //Roo.log(config);
19262     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19263
19264     this.tree = tree;
19265
19266     tree.on('beforeclick', this.beforeNodeClick, this);
19267     tree.getTreeEl().on('mousedown', this.hide, this);
19268     this.on('complete', this.updateNode, this);
19269     this.on('beforestartedit', this.fitToTree, this);
19270     this.on('startedit', this.bindScroll, this, {delay:10});
19271     this.on('specialkey', this.onSpecialKey, this);
19272 };
19273
19274 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19275     /**
19276      * @cfg {String} alignment
19277      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19278      */
19279     alignment: "l-l",
19280     // inherit
19281     autoSize: false,
19282     /**
19283      * @cfg {Boolean} hideEl
19284      * True to hide the bound element while the editor is displayed (defaults to false)
19285      */
19286     hideEl : false,
19287     /**
19288      * @cfg {String} cls
19289      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19290      */
19291     cls: "x-small-editor x-tree-editor",
19292     /**
19293      * @cfg {Boolean} shim
19294      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19295      */
19296     shim:false,
19297     // inherit
19298     shadow:"frame",
19299     /**
19300      * @cfg {Number} maxWidth
19301      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19302      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19303      * scroll and client offsets into account prior to each edit.
19304      */
19305     maxWidth: 250,
19306
19307     editDelay : 350,
19308
19309     // private
19310     fitToTree : function(ed, el){
19311         var td = this.tree.getTreeEl().dom, nd = el.dom;
19312         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19313             td.scrollLeft = nd.offsetLeft;
19314         }
19315         var w = Math.min(
19316                 this.maxWidth,
19317                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19318         this.setSize(w, '');
19319         
19320         return this.fireEvent('beforenodeedit', this, this.editNode);
19321         
19322     },
19323
19324     // private
19325     triggerEdit : function(node){
19326         this.completeEdit();
19327         this.editNode = node;
19328         this.startEdit(node.ui.textNode, node.text);
19329     },
19330
19331     // private
19332     bindScroll : function(){
19333         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19334     },
19335
19336     // private
19337     beforeNodeClick : function(node, e){
19338         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19339         this.lastClick = new Date();
19340         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19341             e.stopEvent();
19342             this.triggerEdit(node);
19343             return false;
19344         }
19345         return true;
19346     },
19347
19348     // private
19349     updateNode : function(ed, value){
19350         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19351         this.editNode.setText(value);
19352     },
19353
19354     // private
19355     onHide : function(){
19356         Roo.tree.TreeEditor.superclass.onHide.call(this);
19357         if(this.editNode){
19358             this.editNode.ui.focus();
19359         }
19360     },
19361
19362     // private
19363     onSpecialKey : function(field, e){
19364         var k = e.getKey();
19365         if(k == e.ESC){
19366             e.stopEvent();
19367             this.cancelEdit();
19368         }else if(k == e.ENTER && !e.hasModifier()){
19369             e.stopEvent();
19370             this.completeEdit();
19371         }
19372     }
19373 });//<Script type="text/javascript">
19374 /*
19375  * Based on:
19376  * Ext JS Library 1.1.1
19377  * Copyright(c) 2006-2007, Ext JS, LLC.
19378  *
19379  * Originally Released Under LGPL - original licence link has changed is not relivant.
19380  *
19381  * Fork - LGPL
19382  * <script type="text/javascript">
19383  */
19384  
19385 /**
19386  * Not documented??? - probably should be...
19387  */
19388
19389 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19390     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19391     
19392     renderElements : function(n, a, targetNode, bulkRender){
19393         //consel.log("renderElements?");
19394         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19395
19396         var t = n.getOwnerTree();
19397         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19398         
19399         var cols = t.columns;
19400         var bw = t.borderWidth;
19401         var c = cols[0];
19402         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19403          var cb = typeof a.checked == "boolean";
19404         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19405         var colcls = 'x-t-' + tid + '-c0';
19406         var buf = [
19407             '<li class="x-tree-node">',
19408             
19409                 
19410                 '<div class="x-tree-node-el ', a.cls,'">',
19411                     // extran...
19412                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19413                 
19414                 
19415                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19416                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19417                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19418                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19419                            (a.iconCls ? ' '+a.iconCls : ''),
19420                            '" unselectable="on" />',
19421                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19422                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19423                              
19424                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19425                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19426                             '<span unselectable="on" qtip="' + tx + '">',
19427                              tx,
19428                              '</span></a>' ,
19429                     '</div>',
19430                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19431                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19432                  ];
19433         for(var i = 1, len = cols.length; i < len; i++){
19434             c = cols[i];
19435             colcls = 'x-t-' + tid + '-c' +i;
19436             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19437             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19438                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19439                       "</div>");
19440          }
19441          
19442          buf.push(
19443             '</a>',
19444             '<div class="x-clear"></div></div>',
19445             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19446             "</li>");
19447         
19448         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19449             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19450                                 n.nextSibling.ui.getEl(), buf.join(""));
19451         }else{
19452             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19453         }
19454         var el = this.wrap.firstChild;
19455         this.elRow = el;
19456         this.elNode = el.firstChild;
19457         this.ranchor = el.childNodes[1];
19458         this.ctNode = this.wrap.childNodes[1];
19459         var cs = el.firstChild.childNodes;
19460         this.indentNode = cs[0];
19461         this.ecNode = cs[1];
19462         this.iconNode = cs[2];
19463         var index = 3;
19464         if(cb){
19465             this.checkbox = cs[3];
19466             index++;
19467         }
19468         this.anchor = cs[index];
19469         
19470         this.textNode = cs[index].firstChild;
19471         
19472         //el.on("click", this.onClick, this);
19473         //el.on("dblclick", this.onDblClick, this);
19474         
19475         
19476        // console.log(this);
19477     },
19478     initEvents : function(){
19479         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19480         
19481             
19482         var a = this.ranchor;
19483
19484         var el = Roo.get(a);
19485
19486         if(Roo.isOpera){ // opera render bug ignores the CSS
19487             el.setStyle("text-decoration", "none");
19488         }
19489
19490         el.on("click", this.onClick, this);
19491         el.on("dblclick", this.onDblClick, this);
19492         el.on("contextmenu", this.onContextMenu, this);
19493         
19494     },
19495     
19496     /*onSelectedChange : function(state){
19497         if(state){
19498             this.focus();
19499             this.addClass("x-tree-selected");
19500         }else{
19501             //this.blur();
19502             this.removeClass("x-tree-selected");
19503         }
19504     },*/
19505     addClass : function(cls){
19506         if(this.elRow){
19507             Roo.fly(this.elRow).addClass(cls);
19508         }
19509         
19510     },
19511     
19512     
19513     removeClass : function(cls){
19514         if(this.elRow){
19515             Roo.fly(this.elRow).removeClass(cls);
19516         }
19517     }
19518
19519     
19520     
19521 });//<Script type="text/javascript">
19522
19523 /*
19524  * Based on:
19525  * Ext JS Library 1.1.1
19526  * Copyright(c) 2006-2007, Ext JS, LLC.
19527  *
19528  * Originally Released Under LGPL - original licence link has changed is not relivant.
19529  *
19530  * Fork - LGPL
19531  * <script type="text/javascript">
19532  */
19533  
19534
19535 /**
19536  * @class Roo.tree.ColumnTree
19537  * @extends Roo.data.TreePanel
19538  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19539  * @cfg {int} borderWidth  compined right/left border allowance
19540  * @constructor
19541  * @param {String/HTMLElement/Element} el The container element
19542  * @param {Object} config
19543  */
19544 Roo.tree.ColumnTree =  function(el, config)
19545 {
19546    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19547    this.addEvents({
19548         /**
19549         * @event resize
19550         * Fire this event on a container when it resizes
19551         * @param {int} w Width
19552         * @param {int} h Height
19553         */
19554        "resize" : true
19555     });
19556     this.on('resize', this.onResize, this);
19557 };
19558
19559 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19560     //lines:false,
19561     
19562     
19563     borderWidth: Roo.isBorderBox ? 0 : 2, 
19564     headEls : false,
19565     
19566     render : function(){
19567         // add the header.....
19568        
19569         Roo.tree.ColumnTree.superclass.render.apply(this);
19570         
19571         this.el.addClass('x-column-tree');
19572         
19573         this.headers = this.el.createChild(
19574             {cls:'x-tree-headers'},this.innerCt.dom);
19575    
19576         var cols = this.columns, c;
19577         var totalWidth = 0;
19578         this.headEls = [];
19579         var  len = cols.length;
19580         for(var i = 0; i < len; i++){
19581              c = cols[i];
19582              totalWidth += c.width;
19583             this.headEls.push(this.headers.createChild({
19584                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19585                  cn: {
19586                      cls:'x-tree-hd-text',
19587                      html: c.header
19588                  },
19589                  style:'width:'+(c.width-this.borderWidth)+'px;'
19590              }));
19591         }
19592         this.headers.createChild({cls:'x-clear'});
19593         // prevent floats from wrapping when clipped
19594         this.headers.setWidth(totalWidth);
19595         //this.innerCt.setWidth(totalWidth);
19596         this.innerCt.setStyle({ overflow: 'auto' });
19597         this.onResize(this.width, this.height);
19598              
19599         
19600     },
19601     onResize : function(w,h)
19602     {
19603         this.height = h;
19604         this.width = w;
19605         // resize cols..
19606         this.innerCt.setWidth(this.width);
19607         this.innerCt.setHeight(this.height-20);
19608         
19609         // headers...
19610         var cols = this.columns, c;
19611         var totalWidth = 0;
19612         var expEl = false;
19613         var len = cols.length;
19614         for(var i = 0; i < len; i++){
19615             c = cols[i];
19616             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19617                 // it's the expander..
19618                 expEl  = this.headEls[i];
19619                 continue;
19620             }
19621             totalWidth += c.width;
19622             
19623         }
19624         if (expEl) {
19625             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19626         }
19627         this.headers.setWidth(w-20);
19628
19629         
19630         
19631         
19632     }
19633 });
19634 /*
19635  * Based on:
19636  * Ext JS Library 1.1.1
19637  * Copyright(c) 2006-2007, Ext JS, LLC.
19638  *
19639  * Originally Released Under LGPL - original licence link has changed is not relivant.
19640  *
19641  * Fork - LGPL
19642  * <script type="text/javascript">
19643  */
19644  
19645 /**
19646  * @class Roo.menu.Menu
19647  * @extends Roo.util.Observable
19648  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19649  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19650  * @constructor
19651  * Creates a new Menu
19652  * @param {Object} config Configuration options
19653  */
19654 Roo.menu.Menu = function(config){
19655     Roo.apply(this, config);
19656     this.id = this.id || Roo.id();
19657     this.addEvents({
19658         /**
19659          * @event beforeshow
19660          * Fires before this menu is displayed
19661          * @param {Roo.menu.Menu} this
19662          */
19663         beforeshow : true,
19664         /**
19665          * @event beforehide
19666          * Fires before this menu is hidden
19667          * @param {Roo.menu.Menu} this
19668          */
19669         beforehide : true,
19670         /**
19671          * @event show
19672          * Fires after this menu is displayed
19673          * @param {Roo.menu.Menu} this
19674          */
19675         show : true,
19676         /**
19677          * @event hide
19678          * Fires after this menu is hidden
19679          * @param {Roo.menu.Menu} this
19680          */
19681         hide : true,
19682         /**
19683          * @event click
19684          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19685          * @param {Roo.menu.Menu} this
19686          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19687          * @param {Roo.EventObject} e
19688          */
19689         click : true,
19690         /**
19691          * @event mouseover
19692          * Fires when the mouse is hovering over this menu
19693          * @param {Roo.menu.Menu} this
19694          * @param {Roo.EventObject} e
19695          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19696          */
19697         mouseover : true,
19698         /**
19699          * @event mouseout
19700          * Fires when the mouse exits this menu
19701          * @param {Roo.menu.Menu} this
19702          * @param {Roo.EventObject} e
19703          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19704          */
19705         mouseout : true,
19706         /**
19707          * @event itemclick
19708          * Fires when a menu item contained in this menu is clicked
19709          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19710          * @param {Roo.EventObject} e
19711          */
19712         itemclick: true
19713     });
19714     if (this.registerMenu) {
19715         Roo.menu.MenuMgr.register(this);
19716     }
19717     
19718     var mis = this.items;
19719     this.items = new Roo.util.MixedCollection();
19720     if(mis){
19721         this.add.apply(this, mis);
19722     }
19723 };
19724
19725 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19726     /**
19727      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19728      */
19729     minWidth : 120,
19730     /**
19731      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19732      * for bottom-right shadow (defaults to "sides")
19733      */
19734     shadow : "sides",
19735     /**
19736      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19737      * this menu (defaults to "tl-tr?")
19738      */
19739     subMenuAlign : "tl-tr?",
19740     /**
19741      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19742      * relative to its element of origin (defaults to "tl-bl?")
19743      */
19744     defaultAlign : "tl-bl?",
19745     /**
19746      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19747      */
19748     allowOtherMenus : false,
19749     /**
19750      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19751      */
19752     registerMenu : true,
19753
19754     hidden:true,
19755
19756     // private
19757     render : function(){
19758         if(this.el){
19759             return;
19760         }
19761         var el = this.el = new Roo.Layer({
19762             cls: "x-menu",
19763             shadow:this.shadow,
19764             constrain: false,
19765             parentEl: this.parentEl || document.body,
19766             zindex:15000
19767         });
19768
19769         this.keyNav = new Roo.menu.MenuNav(this);
19770
19771         if(this.plain){
19772             el.addClass("x-menu-plain");
19773         }
19774         if(this.cls){
19775             el.addClass(this.cls);
19776         }
19777         // generic focus element
19778         this.focusEl = el.createChild({
19779             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19780         });
19781         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19782         ul.on("click", this.onClick, this);
19783         ul.on("mouseover", this.onMouseOver, this);
19784         ul.on("mouseout", this.onMouseOut, this);
19785         this.items.each(function(item){
19786             var li = document.createElement("li");
19787             li.className = "x-menu-list-item";
19788             ul.dom.appendChild(li);
19789             item.render(li, this);
19790         }, this);
19791         this.ul = ul;
19792         this.autoWidth();
19793     },
19794
19795     // private
19796     autoWidth : function(){
19797         var el = this.el, ul = this.ul;
19798         if(!el){
19799             return;
19800         }
19801         var w = this.width;
19802         if(w){
19803             el.setWidth(w);
19804         }else if(Roo.isIE){
19805             el.setWidth(this.minWidth);
19806             var t = el.dom.offsetWidth; // force recalc
19807             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19808         }
19809     },
19810
19811     // private
19812     delayAutoWidth : function(){
19813         if(this.rendered){
19814             if(!this.awTask){
19815                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19816             }
19817             this.awTask.delay(20);
19818         }
19819     },
19820
19821     // private
19822     findTargetItem : function(e){
19823         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19824         if(t && t.menuItemId){
19825             return this.items.get(t.menuItemId);
19826         }
19827     },
19828
19829     // private
19830     onClick : function(e){
19831         var t;
19832         if(t = this.findTargetItem(e)){
19833             t.onClick(e);
19834             this.fireEvent("click", this, t, e);
19835         }
19836     },
19837
19838     // private
19839     setActiveItem : function(item, autoExpand){
19840         if(item != this.activeItem){
19841             if(this.activeItem){
19842                 this.activeItem.deactivate();
19843             }
19844             this.activeItem = item;
19845             item.activate(autoExpand);
19846         }else if(autoExpand){
19847             item.expandMenu();
19848         }
19849     },
19850
19851     // private
19852     tryActivate : function(start, step){
19853         var items = this.items;
19854         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19855             var item = items.get(i);
19856             if(!item.disabled && item.canActivate){
19857                 this.setActiveItem(item, false);
19858                 return item;
19859             }
19860         }
19861         return false;
19862     },
19863
19864     // private
19865     onMouseOver : function(e){
19866         var t;
19867         if(t = this.findTargetItem(e)){
19868             if(t.canActivate && !t.disabled){
19869                 this.setActiveItem(t, true);
19870             }
19871         }
19872         this.fireEvent("mouseover", this, e, t);
19873     },
19874
19875     // private
19876     onMouseOut : function(e){
19877         var t;
19878         if(t = this.findTargetItem(e)){
19879             if(t == this.activeItem && t.shouldDeactivate(e)){
19880                 this.activeItem.deactivate();
19881                 delete this.activeItem;
19882             }
19883         }
19884         this.fireEvent("mouseout", this, e, t);
19885     },
19886
19887     /**
19888      * Read-only.  Returns true if the menu is currently displayed, else false.
19889      * @type Boolean
19890      */
19891     isVisible : function(){
19892         return this.el && !this.hidden;
19893     },
19894
19895     /**
19896      * Displays this menu relative to another element
19897      * @param {String/HTMLElement/Roo.Element} element The element to align to
19898      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19899      * the element (defaults to this.defaultAlign)
19900      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19901      */
19902     show : function(el, pos, parentMenu){
19903         this.parentMenu = parentMenu;
19904         if(!this.el){
19905             this.render();
19906         }
19907         this.fireEvent("beforeshow", this);
19908         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19909     },
19910
19911     /**
19912      * Displays this menu at a specific xy position
19913      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19914      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19915      */
19916     showAt : function(xy, parentMenu, /* private: */_e){
19917         this.parentMenu = parentMenu;
19918         if(!this.el){
19919             this.render();
19920         }
19921         if(_e !== false){
19922             this.fireEvent("beforeshow", this);
19923             xy = this.el.adjustForConstraints(xy);
19924         }
19925         this.el.setXY(xy);
19926         this.el.show();
19927         this.hidden = false;
19928         this.focus();
19929         this.fireEvent("show", this);
19930     },
19931
19932     focus : function(){
19933         if(!this.hidden){
19934             this.doFocus.defer(50, this);
19935         }
19936     },
19937
19938     doFocus : function(){
19939         if(!this.hidden){
19940             this.focusEl.focus();
19941         }
19942     },
19943
19944     /**
19945      * Hides this menu and optionally all parent menus
19946      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19947      */
19948     hide : function(deep){
19949         if(this.el && this.isVisible()){
19950             this.fireEvent("beforehide", this);
19951             if(this.activeItem){
19952                 this.activeItem.deactivate();
19953                 this.activeItem = null;
19954             }
19955             this.el.hide();
19956             this.hidden = true;
19957             this.fireEvent("hide", this);
19958         }
19959         if(deep === true && this.parentMenu){
19960             this.parentMenu.hide(true);
19961         }
19962     },
19963
19964     /**
19965      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19966      * Any of the following are valid:
19967      * <ul>
19968      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19969      * <li>An HTMLElement object which will be converted to a menu item</li>
19970      * <li>A menu item config object that will be created as a new menu item</li>
19971      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19972      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19973      * </ul>
19974      * Usage:
19975      * <pre><code>
19976 // Create the menu
19977 var menu = new Roo.menu.Menu();
19978
19979 // Create a menu item to add by reference
19980 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19981
19982 // Add a bunch of items at once using different methods.
19983 // Only the last item added will be returned.
19984 var item = menu.add(
19985     menuItem,                // add existing item by ref
19986     'Dynamic Item',          // new TextItem
19987     '-',                     // new separator
19988     { text: 'Config Item' }  // new item by config
19989 );
19990 </code></pre>
19991      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19992      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19993      */
19994     add : function(){
19995         var a = arguments, l = a.length, item;
19996         for(var i = 0; i < l; i++){
19997             var el = a[i];
19998             if ((typeof(el) == "object") && el.xtype && el.xns) {
19999                 el = Roo.factory(el, Roo.menu);
20000             }
20001             
20002             if(el.render){ // some kind of Item
20003                 item = this.addItem(el);
20004             }else if(typeof el == "string"){ // string
20005                 if(el == "separator" || el == "-"){
20006                     item = this.addSeparator();
20007                 }else{
20008                     item = this.addText(el);
20009                 }
20010             }else if(el.tagName || el.el){ // element
20011                 item = this.addElement(el);
20012             }else if(typeof el == "object"){ // must be menu item config?
20013                 item = this.addMenuItem(el);
20014             }
20015         }
20016         return item;
20017     },
20018
20019     /**
20020      * Returns this menu's underlying {@link Roo.Element} object
20021      * @return {Roo.Element} The element
20022      */
20023     getEl : function(){
20024         if(!this.el){
20025             this.render();
20026         }
20027         return this.el;
20028     },
20029
20030     /**
20031      * Adds a separator bar to the menu
20032      * @return {Roo.menu.Item} The menu item that was added
20033      */
20034     addSeparator : function(){
20035         return this.addItem(new Roo.menu.Separator());
20036     },
20037
20038     /**
20039      * Adds an {@link Roo.Element} object to the menu
20040      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20041      * @return {Roo.menu.Item} The menu item that was added
20042      */
20043     addElement : function(el){
20044         return this.addItem(new Roo.menu.BaseItem(el));
20045     },
20046
20047     /**
20048      * Adds an existing object based on {@link Roo.menu.Item} to the menu
20049      * @param {Roo.menu.Item} item The menu item to add
20050      * @return {Roo.menu.Item} The menu item that was added
20051      */
20052     addItem : function(item){
20053         this.items.add(item);
20054         if(this.ul){
20055             var li = document.createElement("li");
20056             li.className = "x-menu-list-item";
20057             this.ul.dom.appendChild(li);
20058             item.render(li, this);
20059             this.delayAutoWidth();
20060         }
20061         return item;
20062     },
20063
20064     /**
20065      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20066      * @param {Object} config A MenuItem config object
20067      * @return {Roo.menu.Item} The menu item that was added
20068      */
20069     addMenuItem : function(config){
20070         if(!(config instanceof Roo.menu.Item)){
20071             if(typeof config.checked == "boolean"){ // must be check menu item config?
20072                 config = new Roo.menu.CheckItem(config);
20073             }else{
20074                 config = new Roo.menu.Item(config);
20075             }
20076         }
20077         return this.addItem(config);
20078     },
20079
20080     /**
20081      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20082      * @param {String} text The text to display in the menu item
20083      * @return {Roo.menu.Item} The menu item that was added
20084      */
20085     addText : function(text){
20086         return this.addItem(new Roo.menu.TextItem({ text : text }));
20087     },
20088
20089     /**
20090      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20091      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20092      * @param {Roo.menu.Item} item The menu item to add
20093      * @return {Roo.menu.Item} The menu item that was added
20094      */
20095     insert : function(index, item){
20096         this.items.insert(index, item);
20097         if(this.ul){
20098             var li = document.createElement("li");
20099             li.className = "x-menu-list-item";
20100             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20101             item.render(li, this);
20102             this.delayAutoWidth();
20103         }
20104         return item;
20105     },
20106
20107     /**
20108      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20109      * @param {Roo.menu.Item} item The menu item to remove
20110      */
20111     remove : function(item){
20112         this.items.removeKey(item.id);
20113         item.destroy();
20114     },
20115
20116     /**
20117      * Removes and destroys all items in the menu
20118      */
20119     removeAll : function(){
20120         var f;
20121         while(f = this.items.first()){
20122             this.remove(f);
20123         }
20124     }
20125 });
20126
20127 // MenuNav is a private utility class used internally by the Menu
20128 Roo.menu.MenuNav = function(menu){
20129     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20130     this.scope = this.menu = menu;
20131 };
20132
20133 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20134     doRelay : function(e, h){
20135         var k = e.getKey();
20136         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20137             this.menu.tryActivate(0, 1);
20138             return false;
20139         }
20140         return h.call(this.scope || this, e, this.menu);
20141     },
20142
20143     up : function(e, m){
20144         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20145             m.tryActivate(m.items.length-1, -1);
20146         }
20147     },
20148
20149     down : function(e, m){
20150         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20151             m.tryActivate(0, 1);
20152         }
20153     },
20154
20155     right : function(e, m){
20156         if(m.activeItem){
20157             m.activeItem.expandMenu(true);
20158         }
20159     },
20160
20161     left : function(e, m){
20162         m.hide();
20163         if(m.parentMenu && m.parentMenu.activeItem){
20164             m.parentMenu.activeItem.activate();
20165         }
20166     },
20167
20168     enter : function(e, m){
20169         if(m.activeItem){
20170             e.stopPropagation();
20171             m.activeItem.onClick(e);
20172             m.fireEvent("click", this, m.activeItem);
20173             return true;
20174         }
20175     }
20176 });/*
20177  * Based on:
20178  * Ext JS Library 1.1.1
20179  * Copyright(c) 2006-2007, Ext JS, LLC.
20180  *
20181  * Originally Released Under LGPL - original licence link has changed is not relivant.
20182  *
20183  * Fork - LGPL
20184  * <script type="text/javascript">
20185  */
20186  
20187 /**
20188  * @class Roo.menu.MenuMgr
20189  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20190  * @singleton
20191  */
20192 Roo.menu.MenuMgr = function(){
20193    var menus, active, groups = {}, attached = false, lastShow = new Date();
20194
20195    // private - called when first menu is created
20196    function init(){
20197        menus = {};
20198        active = new Roo.util.MixedCollection();
20199        Roo.get(document).addKeyListener(27, function(){
20200            if(active.length > 0){
20201                hideAll();
20202            }
20203        });
20204    }
20205
20206    // private
20207    function hideAll(){
20208        if(active && active.length > 0){
20209            var c = active.clone();
20210            c.each(function(m){
20211                m.hide();
20212            });
20213        }
20214    }
20215
20216    // private
20217    function onHide(m){
20218        active.remove(m);
20219        if(active.length < 1){
20220            Roo.get(document).un("mousedown", onMouseDown);
20221            attached = false;
20222        }
20223    }
20224
20225    // private
20226    function onShow(m){
20227        var last = active.last();
20228        lastShow = new Date();
20229        active.add(m);
20230        if(!attached){
20231            Roo.get(document).on("mousedown", onMouseDown);
20232            attached = true;
20233        }
20234        if(m.parentMenu){
20235           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20236           m.parentMenu.activeChild = m;
20237        }else if(last && last.isVisible()){
20238           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20239        }
20240    }
20241
20242    // private
20243    function onBeforeHide(m){
20244        if(m.activeChild){
20245            m.activeChild.hide();
20246        }
20247        if(m.autoHideTimer){
20248            clearTimeout(m.autoHideTimer);
20249            delete m.autoHideTimer;
20250        }
20251    }
20252
20253    // private
20254    function onBeforeShow(m){
20255        var pm = m.parentMenu;
20256        if(!pm && !m.allowOtherMenus){
20257            hideAll();
20258        }else if(pm && pm.activeChild && active != m){
20259            pm.activeChild.hide();
20260        }
20261    }
20262
20263    // private
20264    function onMouseDown(e){
20265        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20266            hideAll();
20267        }
20268    }
20269
20270    // private
20271    function onBeforeCheck(mi, state){
20272        if(state){
20273            var g = groups[mi.group];
20274            for(var i = 0, l = g.length; i < l; i++){
20275                if(g[i] != mi){
20276                    g[i].setChecked(false);
20277                }
20278            }
20279        }
20280    }
20281
20282    return {
20283
20284        /**
20285         * Hides all menus that are currently visible
20286         */
20287        hideAll : function(){
20288             hideAll();  
20289        },
20290
20291        // private
20292        register : function(menu){
20293            if(!menus){
20294                init();
20295            }
20296            menus[menu.id] = menu;
20297            menu.on("beforehide", onBeforeHide);
20298            menu.on("hide", onHide);
20299            menu.on("beforeshow", onBeforeShow);
20300            menu.on("show", onShow);
20301            var g = menu.group;
20302            if(g && menu.events["checkchange"]){
20303                if(!groups[g]){
20304                    groups[g] = [];
20305                }
20306                groups[g].push(menu);
20307                menu.on("checkchange", onCheck);
20308            }
20309        },
20310
20311         /**
20312          * Returns a {@link Roo.menu.Menu} object
20313          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20314          * be used to generate and return a new Menu instance.
20315          */
20316        get : function(menu){
20317            if(typeof menu == "string"){ // menu id
20318                return menus[menu];
20319            }else if(menu.events){  // menu instance
20320                return menu;
20321            }else if(typeof menu.length == 'number'){ // array of menu items?
20322                return new Roo.menu.Menu({items:menu});
20323            }else{ // otherwise, must be a config
20324                return new Roo.menu.Menu(menu);
20325            }
20326        },
20327
20328        // private
20329        unregister : function(menu){
20330            delete menus[menu.id];
20331            menu.un("beforehide", onBeforeHide);
20332            menu.un("hide", onHide);
20333            menu.un("beforeshow", onBeforeShow);
20334            menu.un("show", onShow);
20335            var g = menu.group;
20336            if(g && menu.events["checkchange"]){
20337                groups[g].remove(menu);
20338                menu.un("checkchange", onCheck);
20339            }
20340        },
20341
20342        // private
20343        registerCheckable : function(menuItem){
20344            var g = menuItem.group;
20345            if(g){
20346                if(!groups[g]){
20347                    groups[g] = [];
20348                }
20349                groups[g].push(menuItem);
20350                menuItem.on("beforecheckchange", onBeforeCheck);
20351            }
20352        },
20353
20354        // private
20355        unregisterCheckable : function(menuItem){
20356            var g = menuItem.group;
20357            if(g){
20358                groups[g].remove(menuItem);
20359                menuItem.un("beforecheckchange", onBeforeCheck);
20360            }
20361        }
20362    };
20363 }();/*
20364  * Based on:
20365  * Ext JS Library 1.1.1
20366  * Copyright(c) 2006-2007, Ext JS, LLC.
20367  *
20368  * Originally Released Under LGPL - original licence link has changed is not relivant.
20369  *
20370  * Fork - LGPL
20371  * <script type="text/javascript">
20372  */
20373  
20374
20375 /**
20376  * @class Roo.menu.BaseItem
20377  * @extends Roo.Component
20378  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20379  * management and base configuration options shared by all menu components.
20380  * @constructor
20381  * Creates a new BaseItem
20382  * @param {Object} config Configuration options
20383  */
20384 Roo.menu.BaseItem = function(config){
20385     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20386
20387     this.addEvents({
20388         /**
20389          * @event click
20390          * Fires when this item is clicked
20391          * @param {Roo.menu.BaseItem} this
20392          * @param {Roo.EventObject} e
20393          */
20394         click: true,
20395         /**
20396          * @event activate
20397          * Fires when this item is activated
20398          * @param {Roo.menu.BaseItem} this
20399          */
20400         activate : true,
20401         /**
20402          * @event deactivate
20403          * Fires when this item is deactivated
20404          * @param {Roo.menu.BaseItem} this
20405          */
20406         deactivate : true
20407     });
20408
20409     if(this.handler){
20410         this.on("click", this.handler, this.scope, true);
20411     }
20412 };
20413
20414 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20415     /**
20416      * @cfg {Function} handler
20417      * A function that will handle the click event of this menu item (defaults to undefined)
20418      */
20419     /**
20420      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20421      */
20422     canActivate : false,
20423     /**
20424      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20425      */
20426     activeClass : "x-menu-item-active",
20427     /**
20428      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20429      */
20430     hideOnClick : true,
20431     /**
20432      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20433      */
20434     hideDelay : 100,
20435
20436     // private
20437     ctype: "Roo.menu.BaseItem",
20438
20439     // private
20440     actionMode : "container",
20441
20442     // private
20443     render : function(container, parentMenu){
20444         this.parentMenu = parentMenu;
20445         Roo.menu.BaseItem.superclass.render.call(this, container);
20446         this.container.menuItemId = this.id;
20447     },
20448
20449     // private
20450     onRender : function(container, position){
20451         this.el = Roo.get(this.el);
20452         container.dom.appendChild(this.el.dom);
20453     },
20454
20455     // private
20456     onClick : function(e){
20457         if(!this.disabled && this.fireEvent("click", this, e) !== false
20458                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20459             this.handleClick(e);
20460         }else{
20461             e.stopEvent();
20462         }
20463     },
20464
20465     // private
20466     activate : function(){
20467         if(this.disabled){
20468             return false;
20469         }
20470         var li = this.container;
20471         li.addClass(this.activeClass);
20472         this.region = li.getRegion().adjust(2, 2, -2, -2);
20473         this.fireEvent("activate", this);
20474         return true;
20475     },
20476
20477     // private
20478     deactivate : function(){
20479         this.container.removeClass(this.activeClass);
20480         this.fireEvent("deactivate", this);
20481     },
20482
20483     // private
20484     shouldDeactivate : function(e){
20485         return !this.region || !this.region.contains(e.getPoint());
20486     },
20487
20488     // private
20489     handleClick : function(e){
20490         if(this.hideOnClick){
20491             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20492         }
20493     },
20494
20495     // private
20496     expandMenu : function(autoActivate){
20497         // do nothing
20498     },
20499
20500     // private
20501     hideMenu : function(){
20502         // do nothing
20503     }
20504 });/*
20505  * Based on:
20506  * Ext JS Library 1.1.1
20507  * Copyright(c) 2006-2007, Ext JS, LLC.
20508  *
20509  * Originally Released Under LGPL - original licence link has changed is not relivant.
20510  *
20511  * Fork - LGPL
20512  * <script type="text/javascript">
20513  */
20514  
20515 /**
20516  * @class Roo.menu.Adapter
20517  * @extends Roo.menu.BaseItem
20518  * 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.
20519  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20520  * @constructor
20521  * Creates a new Adapter
20522  * @param {Object} config Configuration options
20523  */
20524 Roo.menu.Adapter = function(component, config){
20525     Roo.menu.Adapter.superclass.constructor.call(this, config);
20526     this.component = component;
20527 };
20528 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20529     // private
20530     canActivate : true,
20531
20532     // private
20533     onRender : function(container, position){
20534         this.component.render(container);
20535         this.el = this.component.getEl();
20536     },
20537
20538     // private
20539     activate : function(){
20540         if(this.disabled){
20541             return false;
20542         }
20543         this.component.focus();
20544         this.fireEvent("activate", this);
20545         return true;
20546     },
20547
20548     // private
20549     deactivate : function(){
20550         this.fireEvent("deactivate", this);
20551     },
20552
20553     // private
20554     disable : function(){
20555         this.component.disable();
20556         Roo.menu.Adapter.superclass.disable.call(this);
20557     },
20558
20559     // private
20560     enable : function(){
20561         this.component.enable();
20562         Roo.menu.Adapter.superclass.enable.call(this);
20563     }
20564 });/*
20565  * Based on:
20566  * Ext JS Library 1.1.1
20567  * Copyright(c) 2006-2007, Ext JS, LLC.
20568  *
20569  * Originally Released Under LGPL - original licence link has changed is not relivant.
20570  *
20571  * Fork - LGPL
20572  * <script type="text/javascript">
20573  */
20574
20575 /**
20576  * @class Roo.menu.TextItem
20577  * @extends Roo.menu.BaseItem
20578  * Adds a static text string to a menu, usually used as either a heading or group separator.
20579  * Note: old style constructor with text is still supported.
20580  * 
20581  * @constructor
20582  * Creates a new TextItem
20583  * @param {Object} cfg Configuration
20584  */
20585 Roo.menu.TextItem = function(cfg){
20586     if (typeof(cfg) == 'string') {
20587         this.text = cfg;
20588     } else {
20589         Roo.apply(this,cfg);
20590     }
20591     
20592     Roo.menu.TextItem.superclass.constructor.call(this);
20593 };
20594
20595 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20596     /**
20597      * @cfg {Boolean} text Text to show on item.
20598      */
20599     text : '',
20600     
20601     /**
20602      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20603      */
20604     hideOnClick : false,
20605     /**
20606      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20607      */
20608     itemCls : "x-menu-text",
20609
20610     // private
20611     onRender : function(){
20612         var s = document.createElement("span");
20613         s.className = this.itemCls;
20614         s.innerHTML = this.text;
20615         this.el = s;
20616         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20617     }
20618 });/*
20619  * Based on:
20620  * Ext JS Library 1.1.1
20621  * Copyright(c) 2006-2007, Ext JS, LLC.
20622  *
20623  * Originally Released Under LGPL - original licence link has changed is not relivant.
20624  *
20625  * Fork - LGPL
20626  * <script type="text/javascript">
20627  */
20628
20629 /**
20630  * @class Roo.menu.Separator
20631  * @extends Roo.menu.BaseItem
20632  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20633  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20634  * @constructor
20635  * @param {Object} config Configuration options
20636  */
20637 Roo.menu.Separator = function(config){
20638     Roo.menu.Separator.superclass.constructor.call(this, config);
20639 };
20640
20641 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20642     /**
20643      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20644      */
20645     itemCls : "x-menu-sep",
20646     /**
20647      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20648      */
20649     hideOnClick : false,
20650
20651     // private
20652     onRender : function(li){
20653         var s = document.createElement("span");
20654         s.className = this.itemCls;
20655         s.innerHTML = "&#160;";
20656         this.el = s;
20657         li.addClass("x-menu-sep-li");
20658         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20659     }
20660 });/*
20661  * Based on:
20662  * Ext JS Library 1.1.1
20663  * Copyright(c) 2006-2007, Ext JS, LLC.
20664  *
20665  * Originally Released Under LGPL - original licence link has changed is not relivant.
20666  *
20667  * Fork - LGPL
20668  * <script type="text/javascript">
20669  */
20670 /**
20671  * @class Roo.menu.Item
20672  * @extends Roo.menu.BaseItem
20673  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20674  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20675  * activation and click handling.
20676  * @constructor
20677  * Creates a new Item
20678  * @param {Object} config Configuration options
20679  */
20680 Roo.menu.Item = function(config){
20681     Roo.menu.Item.superclass.constructor.call(this, config);
20682     if(this.menu){
20683         this.menu = Roo.menu.MenuMgr.get(this.menu);
20684     }
20685 };
20686 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20687     
20688     /**
20689      * @cfg {String} text
20690      * The text to show on the menu item.
20691      */
20692     text: '',
20693      /**
20694      * @cfg {String} HTML to render in menu
20695      * The text to show on the menu item (HTML version).
20696      */
20697     html: '',
20698     /**
20699      * @cfg {String} icon
20700      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20701      */
20702     icon: undefined,
20703     /**
20704      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20705      */
20706     itemCls : "x-menu-item",
20707     /**
20708      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20709      */
20710     canActivate : true,
20711     /**
20712      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20713      */
20714     showDelay: 200,
20715     // doc'd in BaseItem
20716     hideDelay: 200,
20717
20718     // private
20719     ctype: "Roo.menu.Item",
20720     
20721     // private
20722     onRender : function(container, position){
20723         var el = document.createElement("a");
20724         el.hideFocus = true;
20725         el.unselectable = "on";
20726         el.href = this.href || "#";
20727         if(this.hrefTarget){
20728             el.target = this.hrefTarget;
20729         }
20730         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20731         
20732         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20733         
20734         el.innerHTML = String.format(
20735                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20736                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20737         this.el = el;
20738         Roo.menu.Item.superclass.onRender.call(this, container, position);
20739     },
20740
20741     /**
20742      * Sets the text to display in this menu item
20743      * @param {String} text The text to display
20744      * @param {Boolean} isHTML true to indicate text is pure html.
20745      */
20746     setText : function(text, isHTML){
20747         if (isHTML) {
20748             this.html = text;
20749         } else {
20750             this.text = text;
20751             this.html = '';
20752         }
20753         if(this.rendered){
20754             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20755      
20756             this.el.update(String.format(
20757                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20758                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20759             this.parentMenu.autoWidth();
20760         }
20761     },
20762
20763     // private
20764     handleClick : function(e){
20765         if(!this.href){ // if no link defined, stop the event automatically
20766             e.stopEvent();
20767         }
20768         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20769     },
20770
20771     // private
20772     activate : function(autoExpand){
20773         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20774             this.focus();
20775             if(autoExpand){
20776                 this.expandMenu();
20777             }
20778         }
20779         return true;
20780     },
20781
20782     // private
20783     shouldDeactivate : function(e){
20784         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20785             if(this.menu && this.menu.isVisible()){
20786                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20787             }
20788             return true;
20789         }
20790         return false;
20791     },
20792
20793     // private
20794     deactivate : function(){
20795         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20796         this.hideMenu();
20797     },
20798
20799     // private
20800     expandMenu : function(autoActivate){
20801         if(!this.disabled && this.menu){
20802             clearTimeout(this.hideTimer);
20803             delete this.hideTimer;
20804             if(!this.menu.isVisible() && !this.showTimer){
20805                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20806             }else if (this.menu.isVisible() && autoActivate){
20807                 this.menu.tryActivate(0, 1);
20808             }
20809         }
20810     },
20811
20812     // private
20813     deferExpand : function(autoActivate){
20814         delete this.showTimer;
20815         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20816         if(autoActivate){
20817             this.menu.tryActivate(0, 1);
20818         }
20819     },
20820
20821     // private
20822     hideMenu : function(){
20823         clearTimeout(this.showTimer);
20824         delete this.showTimer;
20825         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20826             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20827         }
20828     },
20829
20830     // private
20831     deferHide : function(){
20832         delete this.hideTimer;
20833         this.menu.hide();
20834     }
20835 });/*
20836  * Based on:
20837  * Ext JS Library 1.1.1
20838  * Copyright(c) 2006-2007, Ext JS, LLC.
20839  *
20840  * Originally Released Under LGPL - original licence link has changed is not relivant.
20841  *
20842  * Fork - LGPL
20843  * <script type="text/javascript">
20844  */
20845  
20846 /**
20847  * @class Roo.menu.CheckItem
20848  * @extends Roo.menu.Item
20849  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20850  * @constructor
20851  * Creates a new CheckItem
20852  * @param {Object} config Configuration options
20853  */
20854 Roo.menu.CheckItem = function(config){
20855     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20856     this.addEvents({
20857         /**
20858          * @event beforecheckchange
20859          * Fires before the checked value is set, providing an opportunity to cancel if needed
20860          * @param {Roo.menu.CheckItem} this
20861          * @param {Boolean} checked The new checked value that will be set
20862          */
20863         "beforecheckchange" : true,
20864         /**
20865          * @event checkchange
20866          * Fires after the checked value has been set
20867          * @param {Roo.menu.CheckItem} this
20868          * @param {Boolean} checked The checked value that was set
20869          */
20870         "checkchange" : true
20871     });
20872     if(this.checkHandler){
20873         this.on('checkchange', this.checkHandler, this.scope);
20874     }
20875 };
20876 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20877     /**
20878      * @cfg {String} group
20879      * All check items with the same group name will automatically be grouped into a single-select
20880      * radio button group (defaults to '')
20881      */
20882     /**
20883      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20884      */
20885     itemCls : "x-menu-item x-menu-check-item",
20886     /**
20887      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20888      */
20889     groupClass : "x-menu-group-item",
20890
20891     /**
20892      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20893      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20894      * initialized with checked = true will be rendered as checked.
20895      */
20896     checked: false,
20897
20898     // private
20899     ctype: "Roo.menu.CheckItem",
20900
20901     // private
20902     onRender : function(c){
20903         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20904         if(this.group){
20905             this.el.addClass(this.groupClass);
20906         }
20907         Roo.menu.MenuMgr.registerCheckable(this);
20908         if(this.checked){
20909             this.checked = false;
20910             this.setChecked(true, true);
20911         }
20912     },
20913
20914     // private
20915     destroy : function(){
20916         if(this.rendered){
20917             Roo.menu.MenuMgr.unregisterCheckable(this);
20918         }
20919         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20920     },
20921
20922     /**
20923      * Set the checked state of this item
20924      * @param {Boolean} checked The new checked value
20925      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20926      */
20927     setChecked : function(state, suppressEvent){
20928         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20929             if(this.container){
20930                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20931             }
20932             this.checked = state;
20933             if(suppressEvent !== true){
20934                 this.fireEvent("checkchange", this, state);
20935             }
20936         }
20937     },
20938
20939     // private
20940     handleClick : function(e){
20941        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20942            this.setChecked(!this.checked);
20943        }
20944        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20945     }
20946 });/*
20947  * Based on:
20948  * Ext JS Library 1.1.1
20949  * Copyright(c) 2006-2007, Ext JS, LLC.
20950  *
20951  * Originally Released Under LGPL - original licence link has changed is not relivant.
20952  *
20953  * Fork - LGPL
20954  * <script type="text/javascript">
20955  */
20956  
20957 /**
20958  * @class Roo.menu.DateItem
20959  * @extends Roo.menu.Adapter
20960  * A menu item that wraps the {@link Roo.DatPicker} component.
20961  * @constructor
20962  * Creates a new DateItem
20963  * @param {Object} config Configuration options
20964  */
20965 Roo.menu.DateItem = function(config){
20966     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20967     /** The Roo.DatePicker object @type Roo.DatePicker */
20968     this.picker = this.component;
20969     this.addEvents({select: true});
20970     
20971     this.picker.on("render", function(picker){
20972         picker.getEl().swallowEvent("click");
20973         picker.container.addClass("x-menu-date-item");
20974     });
20975
20976     this.picker.on("select", this.onSelect, this);
20977 };
20978
20979 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20980     // private
20981     onSelect : function(picker, date){
20982         this.fireEvent("select", this, date, picker);
20983         Roo.menu.DateItem.superclass.handleClick.call(this);
20984     }
20985 });/*
20986  * Based on:
20987  * Ext JS Library 1.1.1
20988  * Copyright(c) 2006-2007, Ext JS, LLC.
20989  *
20990  * Originally Released Under LGPL - original licence link has changed is not relivant.
20991  *
20992  * Fork - LGPL
20993  * <script type="text/javascript">
20994  */
20995  
20996 /**
20997  * @class Roo.menu.ColorItem
20998  * @extends Roo.menu.Adapter
20999  * A menu item that wraps the {@link Roo.ColorPalette} component.
21000  * @constructor
21001  * Creates a new ColorItem
21002  * @param {Object} config Configuration options
21003  */
21004 Roo.menu.ColorItem = function(config){
21005     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21006     /** The Roo.ColorPalette object @type Roo.ColorPalette */
21007     this.palette = this.component;
21008     this.relayEvents(this.palette, ["select"]);
21009     if(this.selectHandler){
21010         this.on('select', this.selectHandler, this.scope);
21011     }
21012 };
21013 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21014  * Based on:
21015  * Ext JS Library 1.1.1
21016  * Copyright(c) 2006-2007, Ext JS, LLC.
21017  *
21018  * Originally Released Under LGPL - original licence link has changed is not relivant.
21019  *
21020  * Fork - LGPL
21021  * <script type="text/javascript">
21022  */
21023  
21024
21025 /**
21026  * @class Roo.menu.DateMenu
21027  * @extends Roo.menu.Menu
21028  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21029  * @constructor
21030  * Creates a new DateMenu
21031  * @param {Object} config Configuration options
21032  */
21033 Roo.menu.DateMenu = function(config){
21034     Roo.menu.DateMenu.superclass.constructor.call(this, config);
21035     this.plain = true;
21036     var di = new Roo.menu.DateItem(config);
21037     this.add(di);
21038     /**
21039      * The {@link Roo.DatePicker} instance for this DateMenu
21040      * @type DatePicker
21041      */
21042     this.picker = di.picker;
21043     /**
21044      * @event select
21045      * @param {DatePicker} picker
21046      * @param {Date} date
21047      */
21048     this.relayEvents(di, ["select"]);
21049
21050     this.on('beforeshow', function(){
21051         if(this.picker){
21052             this.picker.hideMonthPicker(true);
21053         }
21054     }, this);
21055 };
21056 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21057     cls:'x-date-menu'
21058 });/*
21059  * Based on:
21060  * Ext JS Library 1.1.1
21061  * Copyright(c) 2006-2007, Ext JS, LLC.
21062  *
21063  * Originally Released Under LGPL - original licence link has changed is not relivant.
21064  *
21065  * Fork - LGPL
21066  * <script type="text/javascript">
21067  */
21068  
21069
21070 /**
21071  * @class Roo.menu.ColorMenu
21072  * @extends Roo.menu.Menu
21073  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21074  * @constructor
21075  * Creates a new ColorMenu
21076  * @param {Object} config Configuration options
21077  */
21078 Roo.menu.ColorMenu = function(config){
21079     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21080     this.plain = true;
21081     var ci = new Roo.menu.ColorItem(config);
21082     this.add(ci);
21083     /**
21084      * The {@link Roo.ColorPalette} instance for this ColorMenu
21085      * @type ColorPalette
21086      */
21087     this.palette = ci.palette;
21088     /**
21089      * @event select
21090      * @param {ColorPalette} palette
21091      * @param {String} color
21092      */
21093     this.relayEvents(ci, ["select"]);
21094 };
21095 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21096  * Based on:
21097  * Ext JS Library 1.1.1
21098  * Copyright(c) 2006-2007, Ext JS, LLC.
21099  *
21100  * Originally Released Under LGPL - original licence link has changed is not relivant.
21101  *
21102  * Fork - LGPL
21103  * <script type="text/javascript">
21104  */
21105  
21106 /**
21107  * @class Roo.form.Field
21108  * @extends Roo.BoxComponent
21109  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21110  * @constructor
21111  * Creates a new Field
21112  * @param {Object} config Configuration options
21113  */
21114 Roo.form.Field = function(config){
21115     Roo.form.Field.superclass.constructor.call(this, config);
21116 };
21117
21118 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21119     /**
21120      * @cfg {String} fieldLabel Label to use when rendering a form.
21121      */
21122        /**
21123      * @cfg {String} qtip Mouse over tip
21124      */
21125      
21126     /**
21127      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21128      */
21129     invalidClass : "x-form-invalid",
21130     /**
21131      * @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")
21132      */
21133     invalidText : "The value in this field is invalid",
21134     /**
21135      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21136      */
21137     focusClass : "x-form-focus",
21138     /**
21139      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21140       automatic validation (defaults to "keyup").
21141      */
21142     validationEvent : "keyup",
21143     /**
21144      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21145      */
21146     validateOnBlur : true,
21147     /**
21148      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21149      */
21150     validationDelay : 250,
21151     /**
21152      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21153      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21154      */
21155     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21156     /**
21157      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21158      */
21159     fieldClass : "x-form-field",
21160     /**
21161      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21162      *<pre>
21163 Value         Description
21164 -----------   ----------------------------------------------------------------------
21165 qtip          Display a quick tip when the user hovers over the field
21166 title         Display a default browser title attribute popup
21167 under         Add a block div beneath the field containing the error text
21168 side          Add an error icon to the right of the field with a popup on hover
21169 [element id]  Add the error text directly to the innerHTML of the specified element
21170 </pre>
21171      */
21172     msgTarget : 'qtip',
21173     /**
21174      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21175      */
21176     msgFx : 'normal',
21177
21178     /**
21179      * @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.
21180      */
21181     readOnly : false,
21182
21183     /**
21184      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21185      */
21186     disabled : false,
21187
21188     /**
21189      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21190      */
21191     inputType : undefined,
21192     
21193     /**
21194      * @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).
21195          */
21196         tabIndex : undefined,
21197         
21198     // private
21199     isFormField : true,
21200
21201     // private
21202     hasFocus : false,
21203     /**
21204      * @property {Roo.Element} fieldEl
21205      * Element Containing the rendered Field (with label etc.)
21206      */
21207     /**
21208      * @cfg {Mixed} value A value to initialize this field with.
21209      */
21210     value : undefined,
21211
21212     /**
21213      * @cfg {String} name The field's HTML name attribute.
21214      */
21215     /**
21216      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21217      */
21218
21219         // private ??
21220         initComponent : function(){
21221         Roo.form.Field.superclass.initComponent.call(this);
21222         this.addEvents({
21223             /**
21224              * @event focus
21225              * Fires when this field receives input focus.
21226              * @param {Roo.form.Field} this
21227              */
21228             focus : true,
21229             /**
21230              * @event blur
21231              * Fires when this field loses input focus.
21232              * @param {Roo.form.Field} this
21233              */
21234             blur : true,
21235             /**
21236              * @event specialkey
21237              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21238              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21239              * @param {Roo.form.Field} this
21240              * @param {Roo.EventObject} e The event object
21241              */
21242             specialkey : true,
21243             /**
21244              * @event change
21245              * Fires just before the field blurs if the field value has changed.
21246              * @param {Roo.form.Field} this
21247              * @param {Mixed} newValue The new value
21248              * @param {Mixed} oldValue The original value
21249              */
21250             change : true,
21251             /**
21252              * @event invalid
21253              * Fires after the field has been marked as invalid.
21254              * @param {Roo.form.Field} this
21255              * @param {String} msg The validation message
21256              */
21257             invalid : true,
21258             /**
21259              * @event valid
21260              * Fires after the field has been validated with no errors.
21261              * @param {Roo.form.Field} this
21262              */
21263             valid : true,
21264              /**
21265              * @event keyup
21266              * Fires after the key up
21267              * @param {Roo.form.Field} this
21268              * @param {Roo.EventObject}  e The event Object
21269              */
21270             keyup : true
21271         });
21272     },
21273
21274     /**
21275      * Returns the name attribute of the field if available
21276      * @return {String} name The field name
21277      */
21278     getName: function(){
21279          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21280     },
21281
21282     // private
21283     onRender : function(ct, position){
21284         Roo.form.Field.superclass.onRender.call(this, ct, position);
21285         if(!this.el){
21286             var cfg = this.getAutoCreate();
21287             if(!cfg.name){
21288                 cfg.name = this.name || this.id;
21289             }
21290             if(this.inputType){
21291                 cfg.type = this.inputType;
21292             }
21293             this.el = ct.createChild(cfg, position);
21294         }
21295         var type = this.el.dom.type;
21296         if(type){
21297             if(type == 'password'){
21298                 type = 'text';
21299             }
21300             this.el.addClass('x-form-'+type);
21301         }
21302         if(this.readOnly){
21303             this.el.dom.readOnly = true;
21304         }
21305         if(this.tabIndex !== undefined){
21306             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21307         }
21308
21309         this.el.addClass([this.fieldClass, this.cls]);
21310         this.initValue();
21311     },
21312
21313     /**
21314      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21315      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21316      * @return {Roo.form.Field} this
21317      */
21318     applyTo : function(target){
21319         this.allowDomMove = false;
21320         this.el = Roo.get(target);
21321         this.render(this.el.dom.parentNode);
21322         return this;
21323     },
21324
21325     // private
21326     initValue : function(){
21327         if(this.value !== undefined){
21328             this.setValue(this.value);
21329         }else if(this.el.dom.value.length > 0){
21330             this.setValue(this.el.dom.value);
21331         }
21332     },
21333
21334     /**
21335      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21336      */
21337     isDirty : function() {
21338         if(this.disabled) {
21339             return false;
21340         }
21341         return String(this.getValue()) !== String(this.originalValue);
21342     },
21343
21344     // private
21345     afterRender : function(){
21346         Roo.form.Field.superclass.afterRender.call(this);
21347         this.initEvents();
21348     },
21349
21350     // private
21351     fireKey : function(e){
21352         //Roo.log('field ' + e.getKey());
21353         if(e.isNavKeyPress()){
21354             this.fireEvent("specialkey", this, e);
21355         }
21356     },
21357
21358     /**
21359      * Resets the current field value to the originally loaded value and clears any validation messages
21360      */
21361     reset : function(){
21362         this.setValue(this.originalValue);
21363         this.clearInvalid();
21364     },
21365
21366     // private
21367     initEvents : function(){
21368         // safari killled keypress - so keydown is now used..
21369         this.el.on("keydown" , this.fireKey,  this);
21370         this.el.on("focus", this.onFocus,  this);
21371         this.el.on("blur", this.onBlur,  this);
21372         this.el.relayEvent('keyup', this);
21373
21374         // reference to original value for reset
21375         this.originalValue = this.getValue();
21376     },
21377
21378     // private
21379     onFocus : function(){
21380         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21381             this.el.addClass(this.focusClass);
21382         }
21383         if(!this.hasFocus){
21384             this.hasFocus = true;
21385             this.startValue = this.getValue();
21386             this.fireEvent("focus", this);
21387         }
21388     },
21389
21390     beforeBlur : Roo.emptyFn,
21391
21392     // private
21393     onBlur : function(){
21394         this.beforeBlur();
21395         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21396             this.el.removeClass(this.focusClass);
21397         }
21398         this.hasFocus = false;
21399         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21400             this.validate();
21401         }
21402         var v = this.getValue();
21403         if(String(v) !== String(this.startValue)){
21404             this.fireEvent('change', this, v, this.startValue);
21405         }
21406         this.fireEvent("blur", this);
21407     },
21408
21409     /**
21410      * Returns whether or not the field value is currently valid
21411      * @param {Boolean} preventMark True to disable marking the field invalid
21412      * @return {Boolean} True if the value is valid, else false
21413      */
21414     isValid : function(preventMark){
21415         if(this.disabled){
21416             return true;
21417         }
21418         var restore = this.preventMark;
21419         this.preventMark = preventMark === true;
21420         var v = this.validateValue(this.processValue(this.getRawValue()));
21421         this.preventMark = restore;
21422         return v;
21423     },
21424
21425     /**
21426      * Validates the field value
21427      * @return {Boolean} True if the value is valid, else false
21428      */
21429     validate : function(){
21430         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21431             this.clearInvalid();
21432             return true;
21433         }
21434         return false;
21435     },
21436
21437     processValue : function(value){
21438         return value;
21439     },
21440
21441     // private
21442     // Subclasses should provide the validation implementation by overriding this
21443     validateValue : function(value){
21444         return true;
21445     },
21446
21447     /**
21448      * Mark this field as invalid
21449      * @param {String} msg The validation message
21450      */
21451     markInvalid : function(msg){
21452         if(!this.rendered || this.preventMark){ // not rendered
21453             return;
21454         }
21455         this.el.addClass(this.invalidClass);
21456         msg = msg || this.invalidText;
21457         switch(this.msgTarget){
21458             case 'qtip':
21459                 this.el.dom.qtip = msg;
21460                 this.el.dom.qclass = 'x-form-invalid-tip';
21461                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21462                     Roo.QuickTips.enable();
21463                 }
21464                 break;
21465             case 'title':
21466                 this.el.dom.title = msg;
21467                 break;
21468             case 'under':
21469                 if(!this.errorEl){
21470                     var elp = this.el.findParent('.x-form-element', 5, true);
21471                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21472                     this.errorEl.setWidth(elp.getWidth(true)-20);
21473                 }
21474                 this.errorEl.update(msg);
21475                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21476                 break;
21477             case 'side':
21478                 if(!this.errorIcon){
21479                     var elp = this.el.findParent('.x-form-element', 5, true);
21480                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21481                 }
21482                 this.alignErrorIcon();
21483                 this.errorIcon.dom.qtip = msg;
21484                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21485                 this.errorIcon.show();
21486                 this.on('resize', this.alignErrorIcon, this);
21487                 break;
21488             default:
21489                 var t = Roo.getDom(this.msgTarget);
21490                 t.innerHTML = msg;
21491                 t.style.display = this.msgDisplay;
21492                 break;
21493         }
21494         this.fireEvent('invalid', this, msg);
21495     },
21496
21497     // private
21498     alignErrorIcon : function(){
21499         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21500     },
21501
21502     /**
21503      * Clear any invalid styles/messages for this field
21504      */
21505     clearInvalid : function(){
21506         if(!this.rendered || this.preventMark){ // not rendered
21507             return;
21508         }
21509         this.el.removeClass(this.invalidClass);
21510         switch(this.msgTarget){
21511             case 'qtip':
21512                 this.el.dom.qtip = '';
21513                 break;
21514             case 'title':
21515                 this.el.dom.title = '';
21516                 break;
21517             case 'under':
21518                 if(this.errorEl){
21519                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21520                 }
21521                 break;
21522             case 'side':
21523                 if(this.errorIcon){
21524                     this.errorIcon.dom.qtip = '';
21525                     this.errorIcon.hide();
21526                     this.un('resize', this.alignErrorIcon, this);
21527                 }
21528                 break;
21529             default:
21530                 var t = Roo.getDom(this.msgTarget);
21531                 t.innerHTML = '';
21532                 t.style.display = 'none';
21533                 break;
21534         }
21535         this.fireEvent('valid', this);
21536     },
21537
21538     /**
21539      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21540      * @return {Mixed} value The field value
21541      */
21542     getRawValue : function(){
21543         var v = this.el.getValue();
21544         if(v === this.emptyText){
21545             v = '';
21546         }
21547         return v;
21548     },
21549
21550     /**
21551      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21552      * @return {Mixed} value The field value
21553      */
21554     getValue : function(){
21555         var v = this.el.getValue();
21556         if(v === this.emptyText || v === undefined){
21557             v = '';
21558         }
21559         return v;
21560     },
21561
21562     /**
21563      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21564      * @param {Mixed} value The value to set
21565      */
21566     setRawValue : function(v){
21567         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21568     },
21569
21570     /**
21571      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21572      * @param {Mixed} value The value to set
21573      */
21574     setValue : function(v){
21575         this.value = v;
21576         if(this.rendered){
21577             this.el.dom.value = (v === null || v === undefined ? '' : v);
21578              this.validate();
21579         }
21580     },
21581
21582     adjustSize : function(w, h){
21583         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21584         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21585         return s;
21586     },
21587
21588     adjustWidth : function(tag, w){
21589         tag = tag.toLowerCase();
21590         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21591             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21592                 if(tag == 'input'){
21593                     return w + 2;
21594                 }
21595                 if(tag = 'textarea'){
21596                     return w-2;
21597                 }
21598             }else if(Roo.isOpera){
21599                 if(tag == 'input'){
21600                     return w + 2;
21601                 }
21602                 if(tag = 'textarea'){
21603                     return w-2;
21604                 }
21605             }
21606         }
21607         return w;
21608     }
21609 });
21610
21611
21612 // anything other than normal should be considered experimental
21613 Roo.form.Field.msgFx = {
21614     normal : {
21615         show: function(msgEl, f){
21616             msgEl.setDisplayed('block');
21617         },
21618
21619         hide : function(msgEl, f){
21620             msgEl.setDisplayed(false).update('');
21621         }
21622     },
21623
21624     slide : {
21625         show: function(msgEl, f){
21626             msgEl.slideIn('t', {stopFx:true});
21627         },
21628
21629         hide : function(msgEl, f){
21630             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21631         }
21632     },
21633
21634     slideRight : {
21635         show: function(msgEl, f){
21636             msgEl.fixDisplay();
21637             msgEl.alignTo(f.el, 'tl-tr');
21638             msgEl.slideIn('l', {stopFx:true});
21639         },
21640
21641         hide : function(msgEl, f){
21642             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21643         }
21644     }
21645 };/*
21646  * Based on:
21647  * Ext JS Library 1.1.1
21648  * Copyright(c) 2006-2007, Ext JS, LLC.
21649  *
21650  * Originally Released Under LGPL - original licence link has changed is not relivant.
21651  *
21652  * Fork - LGPL
21653  * <script type="text/javascript">
21654  */
21655  
21656
21657 /**
21658  * @class Roo.form.TextField
21659  * @extends Roo.form.Field
21660  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21661  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21662  * @constructor
21663  * Creates a new TextField
21664  * @param {Object} config Configuration options
21665  */
21666 Roo.form.TextField = function(config){
21667     Roo.form.TextField.superclass.constructor.call(this, config);
21668     this.addEvents({
21669         /**
21670          * @event autosize
21671          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21672          * according to the default logic, but this event provides a hook for the developer to apply additional
21673          * logic at runtime to resize the field if needed.
21674              * @param {Roo.form.Field} this This text field
21675              * @param {Number} width The new field width
21676              */
21677         autosize : true
21678     });
21679 };
21680
21681 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21682     /**
21683      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21684      */
21685     grow : false,
21686     /**
21687      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21688      */
21689     growMin : 30,
21690     /**
21691      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21692      */
21693     growMax : 800,
21694     /**
21695      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21696      */
21697     vtype : null,
21698     /**
21699      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21700      */
21701     maskRe : null,
21702     /**
21703      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21704      */
21705     disableKeyFilter : false,
21706     /**
21707      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21708      */
21709     allowBlank : true,
21710     /**
21711      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21712      */
21713     minLength : 0,
21714     /**
21715      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21716      */
21717     maxLength : Number.MAX_VALUE,
21718     /**
21719      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21720      */
21721     minLengthText : "The minimum length for this field is {0}",
21722     /**
21723      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21724      */
21725     maxLengthText : "The maximum length for this field is {0}",
21726     /**
21727      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21728      */
21729     selectOnFocus : false,
21730     /**
21731      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21732      */
21733     blankText : "This field is required",
21734     /**
21735      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21736      * If available, this function will be called only after the basic validators all return true, and will be passed the
21737      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21738      */
21739     validator : null,
21740     /**
21741      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21742      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21743      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21744      */
21745     regex : null,
21746     /**
21747      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21748      */
21749     regexText : "",
21750     /**
21751      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21752      */
21753     emptyText : null,
21754     /**
21755      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21756      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21757      */
21758     emptyClass : 'x-form-empty-field',
21759
21760     // private
21761     initEvents : function(){
21762         Roo.form.TextField.superclass.initEvents.call(this);
21763         if(this.validationEvent == 'keyup'){
21764             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21765             this.el.on('keyup', this.filterValidation, this);
21766         }
21767         else if(this.validationEvent !== false){
21768             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21769         }
21770         if(this.selectOnFocus || this.emptyText){
21771             this.on("focus", this.preFocus, this);
21772             if(this.emptyText){
21773                 this.on('blur', this.postBlur, this);
21774                 this.applyEmptyText();
21775             }
21776         }
21777         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21778             this.el.on("keypress", this.filterKeys, this);
21779         }
21780         if(this.grow){
21781             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21782             this.el.on("click", this.autoSize,  this);
21783         }
21784     },
21785
21786     processValue : function(value){
21787         if(this.stripCharsRe){
21788             var newValue = value.replace(this.stripCharsRe, '');
21789             if(newValue !== value){
21790                 this.setRawValue(newValue);
21791                 return newValue;
21792             }
21793         }
21794         return value;
21795     },
21796
21797     filterValidation : function(e){
21798         if(!e.isNavKeyPress()){
21799             this.validationTask.delay(this.validationDelay);
21800         }
21801     },
21802
21803     // private
21804     onKeyUp : function(e){
21805         if(!e.isNavKeyPress()){
21806             this.autoSize();
21807         }
21808     },
21809
21810     /**
21811      * Resets the current field value to the originally-loaded value and clears any validation messages.
21812      * Also adds emptyText and emptyClass if the original value was blank.
21813      */
21814     reset : function(){
21815         Roo.form.TextField.superclass.reset.call(this);
21816         this.applyEmptyText();
21817     },
21818
21819     applyEmptyText : function(){
21820         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21821             this.setRawValue(this.emptyText);
21822             this.el.addClass(this.emptyClass);
21823         }
21824     },
21825
21826     // private
21827     preFocus : function(){
21828         if(this.emptyText){
21829             if(this.el.dom.value == this.emptyText){
21830                 this.setRawValue('');
21831             }
21832             this.el.removeClass(this.emptyClass);
21833         }
21834         if(this.selectOnFocus){
21835             this.el.dom.select();
21836         }
21837     },
21838
21839     // private
21840     postBlur : function(){
21841         this.applyEmptyText();
21842     },
21843
21844     // private
21845     filterKeys : function(e){
21846         var k = e.getKey();
21847         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21848             return;
21849         }
21850         var c = e.getCharCode(), cc = String.fromCharCode(c);
21851         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21852             return;
21853         }
21854         if(!this.maskRe.test(cc)){
21855             e.stopEvent();
21856         }
21857     },
21858
21859     setValue : function(v){
21860         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21861             this.el.removeClass(this.emptyClass);
21862         }
21863         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21864         this.applyEmptyText();
21865         this.autoSize();
21866     },
21867
21868     /**
21869      * Validates a value according to the field's validation rules and marks the field as invalid
21870      * if the validation fails
21871      * @param {Mixed} value The value to validate
21872      * @return {Boolean} True if the value is valid, else false
21873      */
21874     validateValue : function(value){
21875         if(value.length < 1 || value === this.emptyText){ // if it's blank
21876              if(this.allowBlank){
21877                 this.clearInvalid();
21878                 return true;
21879              }else{
21880                 this.markInvalid(this.blankText);
21881                 return false;
21882              }
21883         }
21884         if(value.length < this.minLength){
21885             this.markInvalid(String.format(this.minLengthText, this.minLength));
21886             return false;
21887         }
21888         if(value.length > this.maxLength){
21889             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21890             return false;
21891         }
21892         if(this.vtype){
21893             var vt = Roo.form.VTypes;
21894             if(!vt[this.vtype](value, this)){
21895                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21896                 return false;
21897             }
21898         }
21899         if(typeof this.validator == "function"){
21900             var msg = this.validator(value);
21901             if(msg !== true){
21902                 this.markInvalid(msg);
21903                 return false;
21904             }
21905         }
21906         if(this.regex && !this.regex.test(value)){
21907             this.markInvalid(this.regexText);
21908             return false;
21909         }
21910         return true;
21911     },
21912
21913     /**
21914      * Selects text in this field
21915      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21916      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21917      */
21918     selectText : function(start, end){
21919         var v = this.getRawValue();
21920         if(v.length > 0){
21921             start = start === undefined ? 0 : start;
21922             end = end === undefined ? v.length : end;
21923             var d = this.el.dom;
21924             if(d.setSelectionRange){
21925                 d.setSelectionRange(start, end);
21926             }else if(d.createTextRange){
21927                 var range = d.createTextRange();
21928                 range.moveStart("character", start);
21929                 range.moveEnd("character", v.length-end);
21930                 range.select();
21931             }
21932         }
21933     },
21934
21935     /**
21936      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21937      * This only takes effect if grow = true, and fires the autosize event.
21938      */
21939     autoSize : function(){
21940         if(!this.grow || !this.rendered){
21941             return;
21942         }
21943         if(!this.metrics){
21944             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21945         }
21946         var el = this.el;
21947         var v = el.dom.value;
21948         var d = document.createElement('div');
21949         d.appendChild(document.createTextNode(v));
21950         v = d.innerHTML;
21951         d = null;
21952         v += "&#160;";
21953         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21954         this.el.setWidth(w);
21955         this.fireEvent("autosize", this, w);
21956     }
21957 });/*
21958  * Based on:
21959  * Ext JS Library 1.1.1
21960  * Copyright(c) 2006-2007, Ext JS, LLC.
21961  *
21962  * Originally Released Under LGPL - original licence link has changed is not relivant.
21963  *
21964  * Fork - LGPL
21965  * <script type="text/javascript">
21966  */
21967  
21968 /**
21969  * @class Roo.form.Hidden
21970  * @extends Roo.form.TextField
21971  * Simple Hidden element used on forms 
21972  * 
21973  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21974  * 
21975  * @constructor
21976  * Creates a new Hidden form element.
21977  * @param {Object} config Configuration options
21978  */
21979
21980
21981
21982 // easy hidden field...
21983 Roo.form.Hidden = function(config){
21984     Roo.form.Hidden.superclass.constructor.call(this, config);
21985 };
21986   
21987 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21988     fieldLabel:      '',
21989     inputType:      'hidden',
21990     width:          50,
21991     allowBlank:     true,
21992     labelSeparator: '',
21993     hidden:         true,
21994     itemCls :       'x-form-item-display-none'
21995
21996
21997 });
21998
21999
22000 /*
22001  * Based on:
22002  * Ext JS Library 1.1.1
22003  * Copyright(c) 2006-2007, Ext JS, LLC.
22004  *
22005  * Originally Released Under LGPL - original licence link has changed is not relivant.
22006  *
22007  * Fork - LGPL
22008  * <script type="text/javascript">
22009  */
22010  
22011 /**
22012  * @class Roo.form.TriggerField
22013  * @extends Roo.form.TextField
22014  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22015  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22016  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22017  * for which you can provide a custom implementation.  For example:
22018  * <pre><code>
22019 var trigger = new Roo.form.TriggerField();
22020 trigger.onTriggerClick = myTriggerFn;
22021 trigger.applyTo('my-field');
22022 </code></pre>
22023  *
22024  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22025  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22026  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22027  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22028  * @constructor
22029  * Create a new TriggerField.
22030  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22031  * to the base TextField)
22032  */
22033 Roo.form.TriggerField = function(config){
22034     this.mimicing = false;
22035     Roo.form.TriggerField.superclass.constructor.call(this, config);
22036 };
22037
22038 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
22039     /**
22040      * @cfg {String} triggerClass A CSS class to apply to the trigger
22041      */
22042     /**
22043      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22044      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22045      */
22046     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22047     /**
22048      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22049      */
22050     hideTrigger:false,
22051
22052     /** @cfg {Boolean} grow @hide */
22053     /** @cfg {Number} growMin @hide */
22054     /** @cfg {Number} growMax @hide */
22055
22056     /**
22057      * @hide 
22058      * @method
22059      */
22060     autoSize: Roo.emptyFn,
22061     // private
22062     monitorTab : true,
22063     // private
22064     deferHeight : true,
22065
22066     
22067     actionMode : 'wrap',
22068     // private
22069     onResize : function(w, h){
22070         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22071         if(typeof w == 'number'){
22072             var x = w - this.trigger.getWidth();
22073             this.el.setWidth(this.adjustWidth('input', x));
22074             this.trigger.setStyle('left', x+'px');
22075         }
22076     },
22077
22078     // private
22079     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22080
22081     // private
22082     getResizeEl : function(){
22083         return this.wrap;
22084     },
22085
22086     // private
22087     getPositionEl : function(){
22088         return this.wrap;
22089     },
22090
22091     // private
22092     alignErrorIcon : function(){
22093         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22094     },
22095
22096     // private
22097     onRender : function(ct, position){
22098         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22099         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22100         this.trigger = this.wrap.createChild(this.triggerConfig ||
22101                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22102         if(this.hideTrigger){
22103             this.trigger.setDisplayed(false);
22104         }
22105         this.initTrigger();
22106         if(!this.width){
22107             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22108         }
22109     },
22110
22111     // private
22112     initTrigger : function(){
22113         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22114         this.trigger.addClassOnOver('x-form-trigger-over');
22115         this.trigger.addClassOnClick('x-form-trigger-click');
22116     },
22117
22118     // private
22119     onDestroy : function(){
22120         if(this.trigger){
22121             this.trigger.removeAllListeners();
22122             this.trigger.remove();
22123         }
22124         if(this.wrap){
22125             this.wrap.remove();
22126         }
22127         Roo.form.TriggerField.superclass.onDestroy.call(this);
22128     },
22129
22130     // private
22131     onFocus : function(){
22132         Roo.form.TriggerField.superclass.onFocus.call(this);
22133         if(!this.mimicing){
22134             this.wrap.addClass('x-trigger-wrap-focus');
22135             this.mimicing = true;
22136             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22137             if(this.monitorTab){
22138                 this.el.on("keydown", this.checkTab, this);
22139             }
22140         }
22141     },
22142
22143     // private
22144     checkTab : function(e){
22145         if(e.getKey() == e.TAB){
22146             this.triggerBlur();
22147         }
22148     },
22149
22150     // private
22151     onBlur : function(){
22152         // do nothing
22153     },
22154
22155     // private
22156     mimicBlur : function(e, t){
22157         if(!this.wrap.contains(t) && this.validateBlur()){
22158             this.triggerBlur();
22159         }
22160     },
22161
22162     // private
22163     triggerBlur : function(){
22164         this.mimicing = false;
22165         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22166         if(this.monitorTab){
22167             this.el.un("keydown", this.checkTab, this);
22168         }
22169         this.wrap.removeClass('x-trigger-wrap-focus');
22170         Roo.form.TriggerField.superclass.onBlur.call(this);
22171     },
22172
22173     // private
22174     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22175     validateBlur : function(e, t){
22176         return true;
22177     },
22178
22179     // private
22180     onDisable : function(){
22181         Roo.form.TriggerField.superclass.onDisable.call(this);
22182         if(this.wrap){
22183             this.wrap.addClass('x-item-disabled');
22184         }
22185     },
22186
22187     // private
22188     onEnable : function(){
22189         Roo.form.TriggerField.superclass.onEnable.call(this);
22190         if(this.wrap){
22191             this.wrap.removeClass('x-item-disabled');
22192         }
22193     },
22194
22195     // private
22196     onShow : function(){
22197         var ae = this.getActionEl();
22198         
22199         if(ae){
22200             ae.dom.style.display = '';
22201             ae.dom.style.visibility = 'visible';
22202         }
22203     },
22204
22205     // private
22206     
22207     onHide : function(){
22208         var ae = this.getActionEl();
22209         ae.dom.style.display = 'none';
22210     },
22211
22212     /**
22213      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22214      * by an implementing function.
22215      * @method
22216      * @param {EventObject} e
22217      */
22218     onTriggerClick : Roo.emptyFn
22219 });
22220
22221 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22222 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22223 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22224 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22225     initComponent : function(){
22226         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22227
22228         this.triggerConfig = {
22229             tag:'span', cls:'x-form-twin-triggers', cn:[
22230             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22231             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22232         ]};
22233     },
22234
22235     getTrigger : function(index){
22236         return this.triggers[index];
22237     },
22238
22239     initTrigger : function(){
22240         var ts = this.trigger.select('.x-form-trigger', true);
22241         this.wrap.setStyle('overflow', 'hidden');
22242         var triggerField = this;
22243         ts.each(function(t, all, index){
22244             t.hide = function(){
22245                 var w = triggerField.wrap.getWidth();
22246                 this.dom.style.display = 'none';
22247                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22248             };
22249             t.show = function(){
22250                 var w = triggerField.wrap.getWidth();
22251                 this.dom.style.display = '';
22252                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22253             };
22254             var triggerIndex = 'Trigger'+(index+1);
22255
22256             if(this['hide'+triggerIndex]){
22257                 t.dom.style.display = 'none';
22258             }
22259             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22260             t.addClassOnOver('x-form-trigger-over');
22261             t.addClassOnClick('x-form-trigger-click');
22262         }, this);
22263         this.triggers = ts.elements;
22264     },
22265
22266     onTrigger1Click : Roo.emptyFn,
22267     onTrigger2Click : Roo.emptyFn
22268 });/*
22269  * Based on:
22270  * Ext JS Library 1.1.1
22271  * Copyright(c) 2006-2007, Ext JS, LLC.
22272  *
22273  * Originally Released Under LGPL - original licence link has changed is not relivant.
22274  *
22275  * Fork - LGPL
22276  * <script type="text/javascript">
22277  */
22278  
22279 /**
22280  * @class Roo.form.TextArea
22281  * @extends Roo.form.TextField
22282  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22283  * support for auto-sizing.
22284  * @constructor
22285  * Creates a new TextArea
22286  * @param {Object} config Configuration options
22287  */
22288 Roo.form.TextArea = function(config){
22289     Roo.form.TextArea.superclass.constructor.call(this, config);
22290     // these are provided exchanges for backwards compat
22291     // minHeight/maxHeight were replaced by growMin/growMax to be
22292     // compatible with TextField growing config values
22293     if(this.minHeight !== undefined){
22294         this.growMin = this.minHeight;
22295     }
22296     if(this.maxHeight !== undefined){
22297         this.growMax = this.maxHeight;
22298     }
22299 };
22300
22301 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22302     /**
22303      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22304      */
22305     growMin : 60,
22306     /**
22307      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22308      */
22309     growMax: 1000,
22310     /**
22311      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22312      * in the field (equivalent to setting overflow: hidden, defaults to false)
22313      */
22314     preventScrollbars: false,
22315     /**
22316      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22317      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22318      */
22319
22320     // private
22321     onRender : function(ct, position){
22322         if(!this.el){
22323             this.defaultAutoCreate = {
22324                 tag: "textarea",
22325                 style:"width:300px;height:60px;",
22326                 autocomplete: "off"
22327             };
22328         }
22329         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22330         if(this.grow){
22331             this.textSizeEl = Roo.DomHelper.append(document.body, {
22332                 tag: "pre", cls: "x-form-grow-sizer"
22333             });
22334             if(this.preventScrollbars){
22335                 this.el.setStyle("overflow", "hidden");
22336             }
22337             this.el.setHeight(this.growMin);
22338         }
22339     },
22340
22341     onDestroy : function(){
22342         if(this.textSizeEl){
22343             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22344         }
22345         Roo.form.TextArea.superclass.onDestroy.call(this);
22346     },
22347
22348     // private
22349     onKeyUp : function(e){
22350         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22351             this.autoSize();
22352         }
22353     },
22354
22355     /**
22356      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22357      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22358      */
22359     autoSize : function(){
22360         if(!this.grow || !this.textSizeEl){
22361             return;
22362         }
22363         var el = this.el;
22364         var v = el.dom.value;
22365         var ts = this.textSizeEl;
22366
22367         ts.innerHTML = '';
22368         ts.appendChild(document.createTextNode(v));
22369         v = ts.innerHTML;
22370
22371         Roo.fly(ts).setWidth(this.el.getWidth());
22372         if(v.length < 1){
22373             v = "&#160;&#160;";
22374         }else{
22375             if(Roo.isIE){
22376                 v = v.replace(/\n/g, '<p>&#160;</p>');
22377             }
22378             v += "&#160;\n&#160;";
22379         }
22380         ts.innerHTML = v;
22381         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22382         if(h != this.lastHeight){
22383             this.lastHeight = h;
22384             this.el.setHeight(h);
22385             this.fireEvent("autosize", this, h);
22386         }
22387     }
22388 });/*
22389  * Based on:
22390  * Ext JS Library 1.1.1
22391  * Copyright(c) 2006-2007, Ext JS, LLC.
22392  *
22393  * Originally Released Under LGPL - original licence link has changed is not relivant.
22394  *
22395  * Fork - LGPL
22396  * <script type="text/javascript">
22397  */
22398  
22399
22400 /**
22401  * @class Roo.form.NumberField
22402  * @extends Roo.form.TextField
22403  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22404  * @constructor
22405  * Creates a new NumberField
22406  * @param {Object} config Configuration options
22407  */
22408 Roo.form.NumberField = function(config){
22409     Roo.form.NumberField.superclass.constructor.call(this, config);
22410 };
22411
22412 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22413     /**
22414      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22415      */
22416     fieldClass: "x-form-field x-form-num-field",
22417     /**
22418      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22419      */
22420     allowDecimals : true,
22421     /**
22422      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22423      */
22424     decimalSeparator : ".",
22425     /**
22426      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22427      */
22428     decimalPrecision : 2,
22429     /**
22430      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22431      */
22432     allowNegative : true,
22433     /**
22434      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22435      */
22436     minValue : Number.NEGATIVE_INFINITY,
22437     /**
22438      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22439      */
22440     maxValue : Number.MAX_VALUE,
22441     /**
22442      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22443      */
22444     minText : "The minimum value for this field is {0}",
22445     /**
22446      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22447      */
22448     maxText : "The maximum value for this field is {0}",
22449     /**
22450      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22451      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22452      */
22453     nanText : "{0} is not a valid number",
22454
22455     // private
22456     initEvents : function(){
22457         Roo.form.NumberField.superclass.initEvents.call(this);
22458         var allowed = "0123456789";
22459         if(this.allowDecimals){
22460             allowed += this.decimalSeparator;
22461         }
22462         if(this.allowNegative){
22463             allowed += "-";
22464         }
22465         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22466         var keyPress = function(e){
22467             var k = e.getKey();
22468             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22469                 return;
22470             }
22471             var c = e.getCharCode();
22472             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22473                 e.stopEvent();
22474             }
22475         };
22476         this.el.on("keypress", keyPress, this);
22477     },
22478
22479     // private
22480     validateValue : function(value){
22481         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22482             return false;
22483         }
22484         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22485              return true;
22486         }
22487         var num = this.parseValue(value);
22488         if(isNaN(num)){
22489             this.markInvalid(String.format(this.nanText, value));
22490             return false;
22491         }
22492         if(num < this.minValue){
22493             this.markInvalid(String.format(this.minText, this.minValue));
22494             return false;
22495         }
22496         if(num > this.maxValue){
22497             this.markInvalid(String.format(this.maxText, this.maxValue));
22498             return false;
22499         }
22500         return true;
22501     },
22502
22503     getValue : function(){
22504         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22505     },
22506
22507     // private
22508     parseValue : function(value){
22509         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22510         return isNaN(value) ? '' : value;
22511     },
22512
22513     // private
22514     fixPrecision : function(value){
22515         var nan = isNaN(value);
22516         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22517             return nan ? '' : value;
22518         }
22519         return parseFloat(value).toFixed(this.decimalPrecision);
22520     },
22521
22522     setValue : function(v){
22523         v = this.fixPrecision(v);
22524         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22525     },
22526
22527     // private
22528     decimalPrecisionFcn : function(v){
22529         return Math.floor(v);
22530     },
22531
22532     beforeBlur : function(){
22533         var v = this.parseValue(this.getRawValue());
22534         if(v){
22535             this.setValue(v);
22536         }
22537     }
22538 });/*
22539  * Based on:
22540  * Ext JS Library 1.1.1
22541  * Copyright(c) 2006-2007, Ext JS, LLC.
22542  *
22543  * Originally Released Under LGPL - original licence link has changed is not relivant.
22544  *
22545  * Fork - LGPL
22546  * <script type="text/javascript">
22547  */
22548  
22549 /**
22550  * @class Roo.form.DateField
22551  * @extends Roo.form.TriggerField
22552  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22553 * @constructor
22554 * Create a new DateField
22555 * @param {Object} config
22556  */
22557 Roo.form.DateField = function(config){
22558     Roo.form.DateField.superclass.constructor.call(this, config);
22559     
22560       this.addEvents({
22561          
22562         /**
22563          * @event select
22564          * Fires when a date is selected
22565              * @param {Roo.form.DateField} combo This combo box
22566              * @param {Date} date The date selected
22567              */
22568         'select' : true
22569          
22570     });
22571     
22572     
22573     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22574     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22575     this.ddMatch = null;
22576     if(this.disabledDates){
22577         var dd = this.disabledDates;
22578         var re = "(?:";
22579         for(var i = 0; i < dd.length; i++){
22580             re += dd[i];
22581             if(i != dd.length-1) re += "|";
22582         }
22583         this.ddMatch = new RegExp(re + ")");
22584     }
22585 };
22586
22587 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22588     /**
22589      * @cfg {String} format
22590      * The default date format string which can be overriden for localization support.  The format must be
22591      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22592      */
22593     format : "m/d/y",
22594     /**
22595      * @cfg {String} altFormats
22596      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22597      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22598      */
22599     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22600     /**
22601      * @cfg {Array} disabledDays
22602      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22603      */
22604     disabledDays : null,
22605     /**
22606      * @cfg {String} disabledDaysText
22607      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22608      */
22609     disabledDaysText : "Disabled",
22610     /**
22611      * @cfg {Array} disabledDates
22612      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22613      * expression so they are very powerful. Some examples:
22614      * <ul>
22615      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22616      * <li>["03/08", "09/16"] would disable those days for every year</li>
22617      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22618      * <li>["03/../2006"] would disable every day in March 2006</li>
22619      * <li>["^03"] would disable every day in every March</li>
22620      * </ul>
22621      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22622      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22623      */
22624     disabledDates : null,
22625     /**
22626      * @cfg {String} disabledDatesText
22627      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22628      */
22629     disabledDatesText : "Disabled",
22630     /**
22631      * @cfg {Date/String} minValue
22632      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22633      * valid format (defaults to null).
22634      */
22635     minValue : null,
22636     /**
22637      * @cfg {Date/String} maxValue
22638      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22639      * valid format (defaults to null).
22640      */
22641     maxValue : null,
22642     /**
22643      * @cfg {String} minText
22644      * The error text to display when the date in the cell is before minValue (defaults to
22645      * 'The date in this field must be after {minValue}').
22646      */
22647     minText : "The date in this field must be equal to or after {0}",
22648     /**
22649      * @cfg {String} maxText
22650      * The error text to display when the date in the cell is after maxValue (defaults to
22651      * 'The date in this field must be before {maxValue}').
22652      */
22653     maxText : "The date in this field must be equal to or before {0}",
22654     /**
22655      * @cfg {String} invalidText
22656      * The error text to display when the date in the field is invalid (defaults to
22657      * '{value} is not a valid date - it must be in the format {format}').
22658      */
22659     invalidText : "{0} is not a valid date - it must be in the format {1}",
22660     /**
22661      * @cfg {String} triggerClass
22662      * An additional CSS class used to style the trigger button.  The trigger will always get the
22663      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22664      * which displays a calendar icon).
22665      */
22666     triggerClass : 'x-form-date-trigger',
22667     
22668
22669     /**
22670      * @cfg {bool} useIso
22671      * if enabled, then the date field will use a hidden field to store the 
22672      * real value as iso formated date. default (false)
22673      */ 
22674     useIso : false,
22675     /**
22676      * @cfg {String/Object} autoCreate
22677      * A DomHelper element spec, or true for a default element spec (defaults to
22678      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22679      */ 
22680     // private
22681     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22682     
22683     // private
22684     hiddenField: false,
22685     
22686     onRender : function(ct, position)
22687     {
22688         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22689         if (this.useIso) {
22690             this.el.dom.removeAttribute('name'); 
22691             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22692                     'before', true);
22693             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22694             // prevent input submission
22695             this.hiddenName = this.name;
22696         }
22697             
22698             
22699     },
22700     
22701     // private
22702     validateValue : function(value)
22703     {
22704         value = this.formatDate(value);
22705         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22706             return false;
22707         }
22708         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22709              return true;
22710         }
22711         var svalue = value;
22712         value = this.parseDate(value);
22713         if(!value){
22714             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22715             return false;
22716         }
22717         var time = value.getTime();
22718         if(this.minValue && time < this.minValue.getTime()){
22719             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22720             return false;
22721         }
22722         if(this.maxValue && time > this.maxValue.getTime()){
22723             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22724             return false;
22725         }
22726         if(this.disabledDays){
22727             var day = value.getDay();
22728             for(var i = 0; i < this.disabledDays.length; i++) {
22729                 if(day === this.disabledDays[i]){
22730                     this.markInvalid(this.disabledDaysText);
22731                     return false;
22732                 }
22733             }
22734         }
22735         var fvalue = this.formatDate(value);
22736         if(this.ddMatch && this.ddMatch.test(fvalue)){
22737             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22738             return false;
22739         }
22740         return true;
22741     },
22742
22743     // private
22744     // Provides logic to override the default TriggerField.validateBlur which just returns true
22745     validateBlur : function(){
22746         return !this.menu || !this.menu.isVisible();
22747     },
22748
22749     /**
22750      * Returns the current date value of the date field.
22751      * @return {Date} The date value
22752      */
22753     getValue : function(){
22754         
22755         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22756     },
22757
22758     /**
22759      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22760      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22761      * (the default format used is "m/d/y").
22762      * <br />Usage:
22763      * <pre><code>
22764 //All of these calls set the same date value (May 4, 2006)
22765
22766 //Pass a date object:
22767 var dt = new Date('5/4/06');
22768 dateField.setValue(dt);
22769
22770 //Pass a date string (default format):
22771 dateField.setValue('5/4/06');
22772
22773 //Pass a date string (custom format):
22774 dateField.format = 'Y-m-d';
22775 dateField.setValue('2006-5-4');
22776 </code></pre>
22777      * @param {String/Date} date The date or valid date string
22778      */
22779     setValue : function(date){
22780         if (this.hiddenField) {
22781             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22782         }
22783         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22784     },
22785
22786     // private
22787     parseDate : function(value){
22788         if(!value || value instanceof Date){
22789             return value;
22790         }
22791         var v = Date.parseDate(value, this.format);
22792         if(!v && this.altFormats){
22793             if(!this.altFormatsArray){
22794                 this.altFormatsArray = this.altFormats.split("|");
22795             }
22796             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22797                 v = Date.parseDate(value, this.altFormatsArray[i]);
22798             }
22799         }
22800         return v;
22801     },
22802
22803     // private
22804     formatDate : function(date, fmt){
22805         return (!date || !(date instanceof Date)) ?
22806                date : date.dateFormat(fmt || this.format);
22807     },
22808
22809     // private
22810     menuListeners : {
22811         select: function(m, d){
22812             this.setValue(d);
22813             this.fireEvent('select', this, d);
22814         },
22815         show : function(){ // retain focus styling
22816             this.onFocus();
22817         },
22818         hide : function(){
22819             this.focus.defer(10, this);
22820             var ml = this.menuListeners;
22821             this.menu.un("select", ml.select,  this);
22822             this.menu.un("show", ml.show,  this);
22823             this.menu.un("hide", ml.hide,  this);
22824         }
22825     },
22826
22827     // private
22828     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22829     onTriggerClick : function(){
22830         if(this.disabled){
22831             return;
22832         }
22833         if(this.menu == null){
22834             this.menu = new Roo.menu.DateMenu();
22835         }
22836         Roo.apply(this.menu.picker,  {
22837             showClear: this.allowBlank,
22838             minDate : this.minValue,
22839             maxDate : this.maxValue,
22840             disabledDatesRE : this.ddMatch,
22841             disabledDatesText : this.disabledDatesText,
22842             disabledDays : this.disabledDays,
22843             disabledDaysText : this.disabledDaysText,
22844             format : this.format,
22845             minText : String.format(this.minText, this.formatDate(this.minValue)),
22846             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22847         });
22848         this.menu.on(Roo.apply({}, this.menuListeners, {
22849             scope:this
22850         }));
22851         this.menu.picker.setValue(this.getValue() || new Date());
22852         this.menu.show(this.el, "tl-bl?");
22853     },
22854
22855     beforeBlur : function(){
22856         var v = this.parseDate(this.getRawValue());
22857         if(v){
22858             this.setValue(v);
22859         }
22860     }
22861
22862     /** @cfg {Boolean} grow @hide */
22863     /** @cfg {Number} growMin @hide */
22864     /** @cfg {Number} growMax @hide */
22865     /**
22866      * @hide
22867      * @method autoSize
22868      */
22869 });/*
22870  * Based on:
22871  * Ext JS Library 1.1.1
22872  * Copyright(c) 2006-2007, Ext JS, LLC.
22873  *
22874  * Originally Released Under LGPL - original licence link has changed is not relivant.
22875  *
22876  * Fork - LGPL
22877  * <script type="text/javascript">
22878  */
22879  
22880
22881 /**
22882  * @class Roo.form.ComboBox
22883  * @extends Roo.form.TriggerField
22884  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22885  * @constructor
22886  * Create a new ComboBox.
22887  * @param {Object} config Configuration options
22888  */
22889 Roo.form.ComboBox = function(config){
22890     Roo.form.ComboBox.superclass.constructor.call(this, config);
22891     this.addEvents({
22892         /**
22893          * @event expand
22894          * Fires when the dropdown list is expanded
22895              * @param {Roo.form.ComboBox} combo This combo box
22896              */
22897         'expand' : true,
22898         /**
22899          * @event collapse
22900          * Fires when the dropdown list is collapsed
22901              * @param {Roo.form.ComboBox} combo This combo box
22902              */
22903         'collapse' : true,
22904         /**
22905          * @event beforeselect
22906          * Fires before a list item is selected. Return false to cancel the selection.
22907              * @param {Roo.form.ComboBox} combo This combo box
22908              * @param {Roo.data.Record} record The data record returned from the underlying store
22909              * @param {Number} index The index of the selected item in the dropdown list
22910              */
22911         'beforeselect' : true,
22912         /**
22913          * @event select
22914          * Fires when a list item is selected
22915              * @param {Roo.form.ComboBox} combo This combo box
22916              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22917              * @param {Number} index The index of the selected item in the dropdown list
22918              */
22919         'select' : true,
22920         /**
22921          * @event beforequery
22922          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22923          * The event object passed has these properties:
22924              * @param {Roo.form.ComboBox} combo This combo box
22925              * @param {String} query The query
22926              * @param {Boolean} forceAll true to force "all" query
22927              * @param {Boolean} cancel true to cancel the query
22928              * @param {Object} e The query event object
22929              */
22930         'beforequery': true,
22931          /**
22932          * @event add
22933          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22934              * @param {Roo.form.ComboBox} combo This combo box
22935              */
22936         'add' : true,
22937         /**
22938          * @event edit
22939          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22940              * @param {Roo.form.ComboBox} combo This combo box
22941              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22942              */
22943         'edit' : true
22944         
22945         
22946     });
22947     if(this.transform){
22948         this.allowDomMove = false;
22949         var s = Roo.getDom(this.transform);
22950         if(!this.hiddenName){
22951             this.hiddenName = s.name;
22952         }
22953         if(!this.store){
22954             this.mode = 'local';
22955             var d = [], opts = s.options;
22956             for(var i = 0, len = opts.length;i < len; i++){
22957                 var o = opts[i];
22958                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22959                 if(o.selected) {
22960                     this.value = value;
22961                 }
22962                 d.push([value, o.text]);
22963             }
22964             this.store = new Roo.data.SimpleStore({
22965                 'id': 0,
22966                 fields: ['value', 'text'],
22967                 data : d
22968             });
22969             this.valueField = 'value';
22970             this.displayField = 'text';
22971         }
22972         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22973         if(!this.lazyRender){
22974             this.target = true;
22975             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22976             s.parentNode.removeChild(s); // remove it
22977             this.render(this.el.parentNode);
22978         }else{
22979             s.parentNode.removeChild(s); // remove it
22980         }
22981
22982     }
22983     if (this.store) {
22984         this.store = Roo.factory(this.store, Roo.data);
22985     }
22986     
22987     this.selectedIndex = -1;
22988     if(this.mode == 'local'){
22989         if(config.queryDelay === undefined){
22990             this.queryDelay = 10;
22991         }
22992         if(config.minChars === undefined){
22993             this.minChars = 0;
22994         }
22995     }
22996 };
22997
22998 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22999     /**
23000      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23001      */
23002     /**
23003      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23004      * rendering into an Roo.Editor, defaults to false)
23005      */
23006     /**
23007      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23008      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23009      */
23010     /**
23011      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23012      */
23013     /**
23014      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23015      * the dropdown list (defaults to undefined, with no header element)
23016      */
23017
23018      /**
23019      * @cfg {String/Roo.Template} tpl The template to use to render the output
23020      */
23021      
23022     // private
23023     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23024     /**
23025      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23026      */
23027     listWidth: undefined,
23028     /**
23029      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23030      * mode = 'remote' or 'text' if mode = 'local')
23031      */
23032     displayField: undefined,
23033     /**
23034      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23035      * mode = 'remote' or 'value' if mode = 'local'). 
23036      * Note: use of a valueField requires the user make a selection
23037      * in order for a value to be mapped.
23038      */
23039     valueField: undefined,
23040     
23041     
23042     /**
23043      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23044      * field's data value (defaults to the underlying DOM element's name)
23045      */
23046     hiddenName: undefined,
23047     /**
23048      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23049      */
23050     listClass: '',
23051     /**
23052      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23053      */
23054     selectedClass: 'x-combo-selected',
23055     /**
23056      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23057      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23058      * which displays a downward arrow icon).
23059      */
23060     triggerClass : 'x-form-arrow-trigger',
23061     /**
23062      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23063      */
23064     shadow:'sides',
23065     /**
23066      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23067      * anchor positions (defaults to 'tl-bl')
23068      */
23069     listAlign: 'tl-bl?',
23070     /**
23071      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23072      */
23073     maxHeight: 300,
23074     /**
23075      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23076      * query specified by the allQuery config option (defaults to 'query')
23077      */
23078     triggerAction: 'query',
23079     /**
23080      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23081      * (defaults to 4, does not apply if editable = false)
23082      */
23083     minChars : 4,
23084     /**
23085      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23086      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23087      */
23088     typeAhead: false,
23089     /**
23090      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23091      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23092      */
23093     queryDelay: 500,
23094     /**
23095      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23096      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23097      */
23098     pageSize: 0,
23099     /**
23100      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23101      * when editable = true (defaults to false)
23102      */
23103     selectOnFocus:false,
23104     /**
23105      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23106      */
23107     queryParam: 'query',
23108     /**
23109      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23110      * when mode = 'remote' (defaults to 'Loading...')
23111      */
23112     loadingText: 'Loading...',
23113     /**
23114      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23115      */
23116     resizable: false,
23117     /**
23118      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23119      */
23120     handleHeight : 8,
23121     /**
23122      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23123      * traditional select (defaults to true)
23124      */
23125     editable: true,
23126     /**
23127      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23128      */
23129     allQuery: '',
23130     /**
23131      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23132      */
23133     mode: 'remote',
23134     /**
23135      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23136      * listWidth has a higher value)
23137      */
23138     minListWidth : 70,
23139     /**
23140      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23141      * allow the user to set arbitrary text into the field (defaults to false)
23142      */
23143     forceSelection:false,
23144     /**
23145      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23146      * if typeAhead = true (defaults to 250)
23147      */
23148     typeAheadDelay : 250,
23149     /**
23150      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23151      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23152      */
23153     valueNotFoundText : undefined,
23154     /**
23155      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23156      */
23157     blockFocus : false,
23158     
23159     /**
23160      * @cfg {Boolean} disableClear Disable showing of clear button.
23161      */
23162     disableClear : false,
23163     /**
23164      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23165      */
23166     alwaysQuery : false,
23167     
23168     //private
23169     addicon : false,
23170     editicon: false,
23171     
23172     // element that contains real text value.. (when hidden is used..)
23173      
23174     // private
23175     onRender : function(ct, position){
23176         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23177         if(this.hiddenName){
23178             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23179                     'before', true);
23180             this.hiddenField.value =
23181                 this.hiddenValue !== undefined ? this.hiddenValue :
23182                 this.value !== undefined ? this.value : '';
23183
23184             // prevent input submission
23185             this.el.dom.removeAttribute('name');
23186              
23187              
23188         }
23189         if(Roo.isGecko){
23190             this.el.dom.setAttribute('autocomplete', 'off');
23191         }
23192
23193         var cls = 'x-combo-list';
23194
23195         this.list = new Roo.Layer({
23196             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23197         });
23198
23199         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23200         this.list.setWidth(lw);
23201         this.list.swallowEvent('mousewheel');
23202         this.assetHeight = 0;
23203
23204         if(this.title){
23205             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23206             this.assetHeight += this.header.getHeight();
23207         }
23208
23209         this.innerList = this.list.createChild({cls:cls+'-inner'});
23210         this.innerList.on('mouseover', this.onViewOver, this);
23211         this.innerList.on('mousemove', this.onViewMove, this);
23212         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23213         
23214         if(this.allowBlank && !this.pageSize && !this.disableClear){
23215             this.footer = this.list.createChild({cls:cls+'-ft'});
23216             this.pageTb = new Roo.Toolbar(this.footer);
23217            
23218         }
23219         if(this.pageSize){
23220             this.footer = this.list.createChild({cls:cls+'-ft'});
23221             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23222                     {pageSize: this.pageSize});
23223             
23224         }
23225         
23226         if (this.pageTb && this.allowBlank && !this.disableClear) {
23227             var _this = this;
23228             this.pageTb.add(new Roo.Toolbar.Fill(), {
23229                 cls: 'x-btn-icon x-btn-clear',
23230                 text: '&#160;',
23231                 handler: function()
23232                 {
23233                     _this.collapse();
23234                     _this.clearValue();
23235                     _this.onSelect(false, -1);
23236                 }
23237             });
23238         }
23239         if (this.footer) {
23240             this.assetHeight += this.footer.getHeight();
23241         }
23242         
23243
23244         if(!this.tpl){
23245             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23246         }
23247
23248         this.view = new Roo.View(this.innerList, this.tpl, {
23249             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23250         });
23251
23252         this.view.on('click', this.onViewClick, this);
23253
23254         this.store.on('beforeload', this.onBeforeLoad, this);
23255         this.store.on('load', this.onLoad, this);
23256         this.store.on('loadexception', this.onLoadException, this);
23257
23258         if(this.resizable){
23259             this.resizer = new Roo.Resizable(this.list,  {
23260                pinned:true, handles:'se'
23261             });
23262             this.resizer.on('resize', function(r, w, h){
23263                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23264                 this.listWidth = w;
23265                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23266                 this.restrictHeight();
23267             }, this);
23268             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23269         }
23270         if(!this.editable){
23271             this.editable = true;
23272             this.setEditable(false);
23273         }  
23274         
23275         
23276         if (typeof(this.events.add.listeners) != 'undefined') {
23277             
23278             this.addicon = this.wrap.createChild(
23279                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23280        
23281             this.addicon.on('click', function(e) {
23282                 this.fireEvent('add', this);
23283             }, this);
23284         }
23285         if (typeof(this.events.edit.listeners) != 'undefined') {
23286             
23287             this.editicon = this.wrap.createChild(
23288                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23289             if (this.addicon) {
23290                 this.editicon.setStyle('margin-left', '40px');
23291             }
23292             this.editicon.on('click', function(e) {
23293                 
23294                 // we fire even  if inothing is selected..
23295                 this.fireEvent('edit', this, this.lastData );
23296                 
23297             }, this);
23298         }
23299         
23300         
23301         
23302     },
23303
23304     // private
23305     initEvents : function(){
23306         Roo.form.ComboBox.superclass.initEvents.call(this);
23307
23308         this.keyNav = new Roo.KeyNav(this.el, {
23309             "up" : function(e){
23310                 this.inKeyMode = true;
23311                 this.selectPrev();
23312             },
23313
23314             "down" : function(e){
23315                 if(!this.isExpanded()){
23316                     this.onTriggerClick();
23317                 }else{
23318                     this.inKeyMode = true;
23319                     this.selectNext();
23320                 }
23321             },
23322
23323             "enter" : function(e){
23324                 this.onViewClick();
23325                 //return true;
23326             },
23327
23328             "esc" : function(e){
23329                 this.collapse();
23330             },
23331
23332             "tab" : function(e){
23333                 this.onViewClick(false);
23334                 this.fireEvent("specialkey", this, e);
23335                 return true;
23336             },
23337
23338             scope : this,
23339
23340             doRelay : function(foo, bar, hname){
23341                 if(hname == 'down' || this.scope.isExpanded()){
23342                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23343                 }
23344                 return true;
23345             },
23346
23347             forceKeyDown: true
23348         });
23349         this.queryDelay = Math.max(this.queryDelay || 10,
23350                 this.mode == 'local' ? 10 : 250);
23351         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23352         if(this.typeAhead){
23353             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23354         }
23355         if(this.editable !== false){
23356             this.el.on("keyup", this.onKeyUp, this);
23357         }
23358         if(this.forceSelection){
23359             this.on('blur', this.doForce, this);
23360         }
23361     },
23362
23363     onDestroy : function(){
23364         if(this.view){
23365             this.view.setStore(null);
23366             this.view.el.removeAllListeners();
23367             this.view.el.remove();
23368             this.view.purgeListeners();
23369         }
23370         if(this.list){
23371             this.list.destroy();
23372         }
23373         if(this.store){
23374             this.store.un('beforeload', this.onBeforeLoad, this);
23375             this.store.un('load', this.onLoad, this);
23376             this.store.un('loadexception', this.onLoadException, this);
23377         }
23378         Roo.form.ComboBox.superclass.onDestroy.call(this);
23379     },
23380
23381     // private
23382     fireKey : function(e){
23383         if(e.isNavKeyPress() && !this.list.isVisible()){
23384             this.fireEvent("specialkey", this, e);
23385         }
23386     },
23387
23388     // private
23389     onResize: function(w, h){
23390         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23391         
23392         if(typeof w != 'number'){
23393             // we do not handle it!?!?
23394             return;
23395         }
23396         var tw = this.trigger.getWidth();
23397         tw += this.addicon ? this.addicon.getWidth() : 0;
23398         tw += this.editicon ? this.editicon.getWidth() : 0;
23399         var x = w - tw;
23400         this.el.setWidth( this.adjustWidth('input', x));
23401             
23402         this.trigger.setStyle('left', x+'px');
23403         
23404         if(this.list && this.listWidth === undefined){
23405             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23406             this.list.setWidth(lw);
23407             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23408         }
23409         
23410     
23411         
23412     },
23413
23414     /**
23415      * Allow or prevent the user from directly editing the field text.  If false is passed,
23416      * the user will only be able to select from the items defined in the dropdown list.  This method
23417      * is the runtime equivalent of setting the 'editable' config option at config time.
23418      * @param {Boolean} value True to allow the user to directly edit the field text
23419      */
23420     setEditable : function(value){
23421         if(value == this.editable){
23422             return;
23423         }
23424         this.editable = value;
23425         if(!value){
23426             this.el.dom.setAttribute('readOnly', true);
23427             this.el.on('mousedown', this.onTriggerClick,  this);
23428             this.el.addClass('x-combo-noedit');
23429         }else{
23430             this.el.dom.setAttribute('readOnly', false);
23431             this.el.un('mousedown', this.onTriggerClick,  this);
23432             this.el.removeClass('x-combo-noedit');
23433         }
23434     },
23435
23436     // private
23437     onBeforeLoad : function(){
23438         if(!this.hasFocus){
23439             return;
23440         }
23441         this.innerList.update(this.loadingText ?
23442                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23443         this.restrictHeight();
23444         this.selectedIndex = -1;
23445     },
23446
23447     // private
23448     onLoad : function(){
23449         if(!this.hasFocus){
23450             return;
23451         }
23452         if(this.store.getCount() > 0){
23453             this.expand();
23454             this.restrictHeight();
23455             if(this.lastQuery == this.allQuery){
23456                 if(this.editable){
23457                     this.el.dom.select();
23458                 }
23459                 if(!this.selectByValue(this.value, true)){
23460                     this.select(0, true);
23461                 }
23462             }else{
23463                 this.selectNext();
23464                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23465                     this.taTask.delay(this.typeAheadDelay);
23466                 }
23467             }
23468         }else{
23469             this.onEmptyResults();
23470         }
23471         //this.el.focus();
23472     },
23473     // private
23474     onLoadException : function()
23475     {
23476         this.collapse();
23477         Roo.log(this.store.reader.jsonData);
23478         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23479             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23480         }
23481         
23482         
23483     },
23484     // private
23485     onTypeAhead : function(){
23486         if(this.store.getCount() > 0){
23487             var r = this.store.getAt(0);
23488             var newValue = r.data[this.displayField];
23489             var len = newValue.length;
23490             var selStart = this.getRawValue().length;
23491             if(selStart != len){
23492                 this.setRawValue(newValue);
23493                 this.selectText(selStart, newValue.length);
23494             }
23495         }
23496     },
23497
23498     // private
23499     onSelect : function(record, index){
23500         if(this.fireEvent('beforeselect', this, record, index) !== false){
23501             this.setFromData(index > -1 ? record.data : false);
23502             this.collapse();
23503             this.fireEvent('select', this, record, index);
23504         }
23505     },
23506
23507     /**
23508      * Returns the currently selected field value or empty string if no value is set.
23509      * @return {String} value The selected value
23510      */
23511     getValue : function(){
23512         if(this.valueField){
23513             return typeof this.value != 'undefined' ? this.value : '';
23514         }else{
23515             return Roo.form.ComboBox.superclass.getValue.call(this);
23516         }
23517     },
23518
23519     /**
23520      * Clears any text/value currently set in the field
23521      */
23522     clearValue : function(){
23523         if(this.hiddenField){
23524             this.hiddenField.value = '';
23525         }
23526         this.value = '';
23527         this.setRawValue('');
23528         this.lastSelectionText = '';
23529         this.applyEmptyText();
23530     },
23531
23532     /**
23533      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23534      * will be displayed in the field.  If the value does not match the data value of an existing item,
23535      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23536      * Otherwise the field will be blank (although the value will still be set).
23537      * @param {String} value The value to match
23538      */
23539     setValue : function(v){
23540         var text = v;
23541         if(this.valueField){
23542             var r = this.findRecord(this.valueField, v);
23543             if(r){
23544                 text = r.data[this.displayField];
23545             }else if(this.valueNotFoundText !== undefined){
23546                 text = this.valueNotFoundText;
23547             }
23548         }
23549         this.lastSelectionText = text;
23550         if(this.hiddenField){
23551             this.hiddenField.value = v;
23552         }
23553         Roo.form.ComboBox.superclass.setValue.call(this, text);
23554         this.value = v;
23555     },
23556     /**
23557      * @property {Object} the last set data for the element
23558      */
23559     
23560     lastData : false,
23561     /**
23562      * Sets the value of the field based on a object which is related to the record format for the store.
23563      * @param {Object} value the value to set as. or false on reset?
23564      */
23565     setFromData : function(o){
23566         var dv = ''; // display value
23567         var vv = ''; // value value..
23568         this.lastData = o;
23569         if (this.displayField) {
23570             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23571         } else {
23572             // this is an error condition!!!
23573             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23574         }
23575         
23576         if(this.valueField){
23577             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23578         }
23579         if(this.hiddenField){
23580             this.hiddenField.value = vv;
23581             
23582             this.lastSelectionText = dv;
23583             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23584             this.value = vv;
23585             return;
23586         }
23587         // no hidden field.. - we store the value in 'value', but still display
23588         // display field!!!!
23589         this.lastSelectionText = dv;
23590         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23591         this.value = vv;
23592         
23593         
23594     },
23595     // private
23596     reset : function(){
23597         // overridden so that last data is reset..
23598         this.setValue(this.originalValue);
23599         this.clearInvalid();
23600         this.lastData = false;
23601     },
23602     // private
23603     findRecord : function(prop, value){
23604         var record;
23605         if(this.store.getCount() > 0){
23606             this.store.each(function(r){
23607                 if(r.data[prop] == value){
23608                     record = r;
23609                     return false;
23610                 }
23611                 return true;
23612             });
23613         }
23614         return record;
23615     },
23616     
23617     getName: function()
23618     {
23619         // returns hidden if it's set..
23620         if (!this.rendered) {return ''};
23621         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23622         
23623     },
23624     // private
23625     onViewMove : function(e, t){
23626         this.inKeyMode = false;
23627     },
23628
23629     // private
23630     onViewOver : function(e, t){
23631         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23632             return;
23633         }
23634         var item = this.view.findItemFromChild(t);
23635         if(item){
23636             var index = this.view.indexOf(item);
23637             this.select(index, false);
23638         }
23639     },
23640
23641     // private
23642     onViewClick : function(doFocus)
23643     {
23644         var index = this.view.getSelectedIndexes()[0];
23645         var r = this.store.getAt(index);
23646         if(r){
23647             this.onSelect(r, index);
23648         }
23649         if(doFocus !== false && !this.blockFocus){
23650             this.el.focus();
23651         }
23652     },
23653
23654     // private
23655     restrictHeight : function(){
23656         this.innerList.dom.style.height = '';
23657         var inner = this.innerList.dom;
23658         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23659         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23660         this.list.beginUpdate();
23661         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23662         this.list.alignTo(this.el, this.listAlign);
23663         this.list.endUpdate();
23664     },
23665
23666     // private
23667     onEmptyResults : function(){
23668         this.collapse();
23669     },
23670
23671     /**
23672      * Returns true if the dropdown list is expanded, else false.
23673      */
23674     isExpanded : function(){
23675         return this.list.isVisible();
23676     },
23677
23678     /**
23679      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23680      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23681      * @param {String} value The data value of the item to select
23682      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23683      * selected item if it is not currently in view (defaults to true)
23684      * @return {Boolean} True if the value matched an item in the list, else false
23685      */
23686     selectByValue : function(v, scrollIntoView){
23687         if(v !== undefined && v !== null){
23688             var r = this.findRecord(this.valueField || this.displayField, v);
23689             if(r){
23690                 this.select(this.store.indexOf(r), scrollIntoView);
23691                 return true;
23692             }
23693         }
23694         return false;
23695     },
23696
23697     /**
23698      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23699      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23700      * @param {Number} index The zero-based index of the list item to select
23701      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23702      * selected item if it is not currently in view (defaults to true)
23703      */
23704     select : function(index, scrollIntoView){
23705         this.selectedIndex = index;
23706         this.view.select(index);
23707         if(scrollIntoView !== false){
23708             var el = this.view.getNode(index);
23709             if(el){
23710                 this.innerList.scrollChildIntoView(el, false);
23711             }
23712         }
23713     },
23714
23715     // private
23716     selectNext : function(){
23717         var ct = this.store.getCount();
23718         if(ct > 0){
23719             if(this.selectedIndex == -1){
23720                 this.select(0);
23721             }else if(this.selectedIndex < ct-1){
23722                 this.select(this.selectedIndex+1);
23723             }
23724         }
23725     },
23726
23727     // private
23728     selectPrev : function(){
23729         var ct = this.store.getCount();
23730         if(ct > 0){
23731             if(this.selectedIndex == -1){
23732                 this.select(0);
23733             }else if(this.selectedIndex != 0){
23734                 this.select(this.selectedIndex-1);
23735             }
23736         }
23737     },
23738
23739     // private
23740     onKeyUp : function(e){
23741         if(this.editable !== false && !e.isSpecialKey()){
23742             this.lastKey = e.getKey();
23743             this.dqTask.delay(this.queryDelay);
23744         }
23745     },
23746
23747     // private
23748     validateBlur : function(){
23749         return !this.list || !this.list.isVisible();   
23750     },
23751
23752     // private
23753     initQuery : function(){
23754         this.doQuery(this.getRawValue());
23755     },
23756
23757     // private
23758     doForce : function(){
23759         if(this.el.dom.value.length > 0){
23760             this.el.dom.value =
23761                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23762             this.applyEmptyText();
23763         }
23764     },
23765
23766     /**
23767      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23768      * query allowing the query action to be canceled if needed.
23769      * @param {String} query The SQL query to execute
23770      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23771      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23772      * saved in the current store (defaults to false)
23773      */
23774     doQuery : function(q, forceAll){
23775         if(q === undefined || q === null){
23776             q = '';
23777         }
23778         var qe = {
23779             query: q,
23780             forceAll: forceAll,
23781             combo: this,
23782             cancel:false
23783         };
23784         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23785             return false;
23786         }
23787         q = qe.query;
23788         forceAll = qe.forceAll;
23789         if(forceAll === true || (q.length >= this.minChars)){
23790             if(this.lastQuery != q || this.alwaysQuery){
23791                 this.lastQuery = q;
23792                 if(this.mode == 'local'){
23793                     this.selectedIndex = -1;
23794                     if(forceAll){
23795                         this.store.clearFilter();
23796                     }else{
23797                         this.store.filter(this.displayField, q);
23798                     }
23799                     this.onLoad();
23800                 }else{
23801                     this.store.baseParams[this.queryParam] = q;
23802                     this.store.load({
23803                         params: this.getParams(q)
23804                     });
23805                     this.expand();
23806                 }
23807             }else{
23808                 this.selectedIndex = -1;
23809                 this.onLoad();   
23810             }
23811         }
23812     },
23813
23814     // private
23815     getParams : function(q){
23816         var p = {};
23817         //p[this.queryParam] = q;
23818         if(this.pageSize){
23819             p.start = 0;
23820             p.limit = this.pageSize;
23821         }
23822         return p;
23823     },
23824
23825     /**
23826      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23827      */
23828     collapse : function(){
23829         if(!this.isExpanded()){
23830             return;
23831         }
23832         this.list.hide();
23833         Roo.get(document).un('mousedown', this.collapseIf, this);
23834         Roo.get(document).un('mousewheel', this.collapseIf, this);
23835         if (!this.editable) {
23836             Roo.get(document).un('keydown', this.listKeyPress, this);
23837         }
23838         this.fireEvent('collapse', this);
23839     },
23840
23841     // private
23842     collapseIf : function(e){
23843         if(!e.within(this.wrap) && !e.within(this.list)){
23844             this.collapse();
23845         }
23846     },
23847
23848     /**
23849      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23850      */
23851     expand : function(){
23852         if(this.isExpanded() || !this.hasFocus){
23853             return;
23854         }
23855         this.list.alignTo(this.el, this.listAlign);
23856         this.list.show();
23857         Roo.get(document).on('mousedown', this.collapseIf, this);
23858         Roo.get(document).on('mousewheel', this.collapseIf, this);
23859         if (!this.editable) {
23860             Roo.get(document).on('keydown', this.listKeyPress, this);
23861         }
23862         
23863         this.fireEvent('expand', this);
23864     },
23865
23866     // private
23867     // Implements the default empty TriggerField.onTriggerClick function
23868     onTriggerClick : function(){
23869         if(this.disabled){
23870             return;
23871         }
23872         if(this.isExpanded()){
23873             this.collapse();
23874             if (!this.blockFocus) {
23875                 this.el.focus();
23876             }
23877             
23878         }else {
23879             this.hasFocus = true;
23880             if(this.triggerAction == 'all') {
23881                 this.doQuery(this.allQuery, true);
23882             } else {
23883                 this.doQuery(this.getRawValue());
23884             }
23885             if (!this.blockFocus) {
23886                 this.el.focus();
23887             }
23888         }
23889     },
23890     listKeyPress : function(e)
23891     {
23892         //Roo.log('listkeypress');
23893         // scroll to first matching element based on key pres..
23894         if (e.isSpecialKey()) {
23895             return false;
23896         }
23897         var k = String.fromCharCode(e.getKey()).toUpperCase();
23898         //Roo.log(k);
23899         var match  = false;
23900         var csel = this.view.getSelectedNodes();
23901         var cselitem = false;
23902         if (csel.length) {
23903             var ix = this.view.indexOf(csel[0]);
23904             cselitem  = this.store.getAt(ix);
23905             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23906                 cselitem = false;
23907             }
23908             
23909         }
23910         
23911         this.store.each(function(v) { 
23912             if (cselitem) {
23913                 // start at existing selection.
23914                 if (cselitem.id == v.id) {
23915                     cselitem = false;
23916                 }
23917                 return;
23918             }
23919                 
23920             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23921                 match = this.store.indexOf(v);
23922                 return false;
23923             }
23924         }, this);
23925         
23926         if (match === false) {
23927             return true; // no more action?
23928         }
23929         // scroll to?
23930         this.view.select(match);
23931         var sn = Roo.get(this.view.getSelectedNodes()[0])
23932         sn.scrollIntoView(sn.dom.parentNode, false);
23933     }
23934
23935     /** 
23936     * @cfg {Boolean} grow 
23937     * @hide 
23938     */
23939     /** 
23940     * @cfg {Number} growMin 
23941     * @hide 
23942     */
23943     /** 
23944     * @cfg {Number} growMax 
23945     * @hide 
23946     */
23947     /**
23948      * @hide
23949      * @method autoSize
23950      */
23951 });/*
23952  * Copyright(c) 2010-2012, Roo J Solutions Limited
23953  *
23954  * Licence LGPL
23955  *
23956  */
23957
23958 /**
23959  * @class Roo.form.ComboBoxArray
23960  * @extends Roo.form.TextField
23961  * A facebook style adder... for lists of email / people / countries  etc...
23962  * pick multiple items from a combo box, and shows each one.
23963  *
23964  *  Fred [x]  Brian [x]  [Pick another |v]
23965  *
23966  *
23967  *  For this to work: it needs various extra information
23968  *    - normal combo problay has
23969  *      name, hiddenName
23970  *    + displayField, valueField
23971  *
23972  *    For our purpose...
23973  *
23974  *
23975  *   If we change from 'extends' to wrapping...
23976  *   
23977  *  
23978  *
23979  
23980  
23981  * @constructor
23982  * Create a new ComboBoxArray.
23983  * @param {Object} config Configuration options
23984  */
23985  
23986
23987 Roo.form.ComboBoxArray = function(config)
23988 {
23989     
23990     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
23991     
23992     this.items = new Roo.util.MixedCollection(false);
23993     
23994     // construct the child combo...
23995     
23996     
23997     
23998     
23999    
24000     
24001 }
24002
24003  
24004 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24005
24006     /**
24007      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24008      */
24009     
24010     lastData : false,
24011     
24012     // behavies liek a hiddne field
24013     inputType:      'hidden',
24014     /**
24015      * @cfg {Number} width The width of the box that displays the selected element
24016      */ 
24017     width:          300,
24018
24019     
24020     
24021     /**
24022      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24023      */
24024     name : false,
24025     /**
24026      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24027      */
24028     hiddenName : false,
24029     
24030     
24031     // private the array of items that are displayed..
24032     items  : false,
24033     // private - the hidden field el.
24034     hiddenEl : false,
24035     // private - the filed el..
24036     el : false,
24037     
24038     //validateValue : function() { return true; }, // all values are ok!
24039     //onAddClick: function() { },
24040     
24041     onRender : function(ct, position) 
24042     {
24043         
24044         // create the standard hidden element
24045         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24046         
24047         
24048         // give fake names to child combo;
24049         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24050         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24051         
24052         this.combo = Roo.factory(this.combo, Roo.form);
24053         this.combo.onRender(ct, position);
24054         
24055         // assigned so form know we need to do this..
24056         this.store          = this.combo.store;
24057         this.valueField     = this.combo.valueField;
24058         this.displayField   = this.combo.displayField ;
24059         
24060         
24061         this.combo.wrap.addClass('x-cbarray-grp');
24062         
24063         var cbwrap = this.combo.wrap.createChild(
24064             {tag: 'div', cls: 'x-cbarray-cb'},
24065             this.combo.el.dom
24066         );
24067         
24068              
24069         this.hiddenEl = this.combo.wrap.createChild({
24070             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24071         });
24072         this.el = this.combo.wrap.createChild({
24073             tag: 'input',  type:'hidden' , name: this.name, value : ''
24074         });
24075          //   this.el.dom.removeAttribute("name");
24076         
24077         
24078         this.outerWrap = this.combo.wrap;
24079         this.wrap = cbwrap;
24080         
24081         this.outerWrap.setWidth(this.width);
24082         this.outerWrap.dom.removeChild(this.el.dom);
24083         
24084         this.wrap.dom.appendChild(this.el.dom);
24085         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24086         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24087         
24088         this.combo.trigger.setStyle('position','relative');
24089         this.combo.trigger.setStyle('left', '0px');
24090         this.combo.trigger.setStyle('top', '2px');
24091         
24092         this.combo.el.setStyle('vertical-align', 'text-bottom');
24093         
24094         //this.trigger.setStyle('vertical-align', 'top');
24095         
24096         // this should use the code from combo really... on('add' ....)
24097         if (this.adder) {
24098             
24099         
24100             this.adder = this.outerWrap.createChild(
24101                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24102             var _t = this;
24103             this.adder.on('click', function(e) {
24104                 _t.fireEvent('adderclick', this, e);
24105             }, _t);
24106         }
24107         //var _t = this;
24108         //this.adder.on('click', this.onAddClick, _t);
24109         
24110         
24111         this.combo.on('select', function(cb, rec, ix) {
24112             this.addItem(rec.data);
24113             
24114             cb.setValue('');
24115             cb.el.dom.value = '';
24116             //cb.lastData = rec.data;
24117             // add to list
24118             
24119         }, this);
24120         
24121         
24122     },
24123     
24124     
24125     getName: function()
24126     {
24127         // returns hidden if it's set..
24128         if (!this.rendered) {return ''};
24129         return  this.hiddenName ? this.hiddenName : this.name;
24130         
24131     },
24132     
24133     
24134     onResize: function(w, h){
24135         
24136         return;
24137         // not sure if this is needed..
24138         //this.combo.onResize(w,h);
24139         
24140         if(typeof w != 'number'){
24141             // we do not handle it!?!?
24142             return;
24143         }
24144         var tw = this.combo.trigger.getWidth();
24145         tw += this.addicon ? this.addicon.getWidth() : 0;
24146         tw += this.editicon ? this.editicon.getWidth() : 0;
24147         var x = w - tw;
24148         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24149             
24150         this.combo.trigger.setStyle('left', '0px');
24151         
24152         if(this.list && this.listWidth === undefined){
24153             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24154             this.list.setWidth(lw);
24155             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24156         }
24157         
24158     
24159         
24160     },
24161     
24162     addItem: function(rec)
24163     {
24164         var valueField = this.combo.valueField;
24165         var displayField = this.combo.displayField;
24166         if (this.items.indexOfKey(rec[valueField]) > -1) {
24167             //console.log("GOT " + rec.data.id);
24168             return;
24169         }
24170         
24171         var x = new Roo.form.ComboBoxArray.Item({
24172             //id : rec[this.idField],
24173             data : rec,
24174             displayField : displayField ,
24175             tipField : displayField ,
24176             cb : this
24177         });
24178         // use the 
24179         this.items.add(rec[valueField],x);
24180         // add it before the element..
24181         this.updateHiddenEl();
24182         x.render(this.outerWrap, this.wrap.dom);
24183         // add the image handler..
24184     },
24185     
24186     updateHiddenEl : function()
24187     {
24188         this.validate();
24189         if (!this.hiddenEl) {
24190             return;
24191         }
24192         var ar = [];
24193         var idField = this.combo.valueField;
24194         
24195         this.items.each(function(f) {
24196             ar.push(f.data[idField]);
24197            
24198         });
24199         this.hiddenEl.dom.value = ar.join(',');
24200         this.validate();
24201     },
24202     
24203     reset : function()
24204     {
24205         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24206         this.items.each(function(f) {
24207            f.remove(); 
24208         });
24209         this.el.dom.value = '';
24210         if (this.hiddenEl) {
24211             this.hiddenEl.dom.value = '';
24212         }
24213         
24214     },
24215     getValue: function()
24216     {
24217         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24218     },
24219     setValue: function(v) // not a valid action - must use addItems..
24220     {
24221          
24222         this.reset();
24223         
24224         
24225         
24226         if (this.store.isLocal && (typeof(v) == 'string')) {
24227             // then we can use the store to find the values..
24228             // comma seperated at present.. this needs to allow JSON based encoding..
24229             this.hiddenEl.value  = v;
24230             var v_ar = [];
24231             Roo.each(v.split(','), function(k) {
24232                 Roo.log("CHECK " + this.valueField + ',' + k);
24233                 var li = this.store.query(this.valueField, k);
24234                 if (!li.length) {
24235                     return;
24236                 }
24237                 add = {};
24238                 add[this.valueField] = k;
24239                 add[this.displayField] = li.item(0).data[this.displayField];
24240                 
24241                 this.addItem(add);
24242             }, this) 
24243              
24244         }
24245         if (typeof(v) == 'object') {
24246             // then let's assume it's an array of objects..
24247             Roo.each(v, function(l) {
24248                 this.addItem(l);
24249             }, this);
24250              
24251         }
24252         
24253         
24254     },
24255     setFromData: function(v)
24256     {
24257         // this recieves an object, if setValues is called.
24258         this.reset();
24259         this.el.dom.value = v[this.displayField];
24260         this.hiddenEl.dom.value = v[this.valueField];
24261         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24262             return;
24263         }
24264         var keys = v[this.valueField].split(',');
24265         var display = v[this.displayField].split(',');
24266         for (var i = 0 ; i < keys.length; i++) {
24267             
24268             add = {};
24269             add[this.valueField] = keys[i];
24270             add[this.displayField] = display[i];
24271             this.addItem(add);
24272         }
24273       
24274         
24275     },
24276     
24277     
24278     validateValue : function(value){
24279         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24280         
24281     }
24282     
24283 });
24284
24285
24286
24287 /**
24288  * @class Roo.form.ComboBoxArray.Item
24289  * @extends Roo.BoxComponent
24290  * A selected item in the list
24291  *  Fred [x]  Brian [x]  [Pick another |v]
24292  * 
24293  * @constructor
24294  * Create a new item.
24295  * @param {Object} config Configuration options
24296  */
24297  
24298 Roo.form.ComboBoxArray.Item = function(config) {
24299     config.id = Roo.id();
24300     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24301 }
24302
24303 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24304     data : {},
24305     cb: false,
24306     displayField : false,
24307     tipField : false,
24308     
24309     
24310     defaultAutoCreate : {
24311         tag: 'div',
24312         cls: 'x-cbarray-item',
24313         cn : [ 
24314             { tag: 'div' },
24315             {
24316                 tag: 'img',
24317                 width:16,
24318                 height : 16,
24319                 src : Roo.BLANK_IMAGE_URL ,
24320                 align: 'center'
24321             }
24322         ]
24323         
24324     },
24325     
24326  
24327     onRender : function(ct, position)
24328     {
24329         Roo.form.Field.superclass.onRender.call(this, ct, position);
24330         
24331         if(!this.el){
24332             var cfg = this.getAutoCreate();
24333             this.el = ct.createChild(cfg, position);
24334         }
24335         
24336         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24337         
24338         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24339             this.cb.renderer(this.data) :
24340             String.format('{0}',this.data[this.displayField]);
24341         
24342             
24343         this.el.child('div').dom.setAttribute('qtip',
24344                         String.format('{0}',this.data[this.tipField])
24345         );
24346         
24347         this.el.child('img').on('click', this.remove, this);
24348         
24349     },
24350    
24351     remove : function()
24352     {
24353         
24354         this.cb.items.remove(this);
24355         this.el.child('img').un('click', this.remove, this);
24356         this.el.remove();
24357         this.cb.updateHiddenEl();
24358     }
24359     
24360     
24361 });/*
24362  * Based on:
24363  * Ext JS Library 1.1.1
24364  * Copyright(c) 2006-2007, Ext JS, LLC.
24365  *
24366  * Originally Released Under LGPL - original licence link has changed is not relivant.
24367  *
24368  * Fork - LGPL
24369  * <script type="text/javascript">
24370  */
24371 /**
24372  * @class Roo.form.Checkbox
24373  * @extends Roo.form.Field
24374  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24375  * @constructor
24376  * Creates a new Checkbox
24377  * @param {Object} config Configuration options
24378  */
24379 Roo.form.Checkbox = function(config){
24380     Roo.form.Checkbox.superclass.constructor.call(this, config);
24381     this.addEvents({
24382         /**
24383          * @event check
24384          * Fires when the checkbox is checked or unchecked.
24385              * @param {Roo.form.Checkbox} this This checkbox
24386              * @param {Boolean} checked The new checked value
24387              */
24388         check : true
24389     });
24390 };
24391
24392 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24393     /**
24394      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24395      */
24396     focusClass : undefined,
24397     /**
24398      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24399      */
24400     fieldClass: "x-form-field",
24401     /**
24402      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24403      */
24404     checked: false,
24405     /**
24406      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24407      * {tag: "input", type: "checkbox", autocomplete: "off"})
24408      */
24409     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24410     /**
24411      * @cfg {String} boxLabel The text that appears beside the checkbox
24412      */
24413     boxLabel : "",
24414     /**
24415      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24416      */  
24417     inputValue : '1',
24418     /**
24419      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24420      */
24421      valueOff: '0', // value when not checked..
24422
24423     actionMode : 'viewEl', 
24424     //
24425     // private
24426     itemCls : 'x-menu-check-item x-form-item',
24427     groupClass : 'x-menu-group-item',
24428     inputType : 'hidden',
24429     
24430     
24431     inSetChecked: false, // check that we are not calling self...
24432     
24433     inputElement: false, // real input element?
24434     basedOn: false, // ????
24435     
24436     isFormField: true, // not sure where this is needed!!!!
24437
24438     onResize : function(){
24439         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24440         if(!this.boxLabel){
24441             this.el.alignTo(this.wrap, 'c-c');
24442         }
24443     },
24444
24445     initEvents : function(){
24446         Roo.form.Checkbox.superclass.initEvents.call(this);
24447         this.el.on("click", this.onClick,  this);
24448         this.el.on("change", this.onClick,  this);
24449     },
24450
24451
24452     getResizeEl : function(){
24453         return this.wrap;
24454     },
24455
24456     getPositionEl : function(){
24457         return this.wrap;
24458     },
24459
24460     // private
24461     onRender : function(ct, position){
24462         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24463         /*
24464         if(this.inputValue !== undefined){
24465             this.el.dom.value = this.inputValue;
24466         }
24467         */
24468         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24469         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24470         var viewEl = this.wrap.createChild({ 
24471             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24472         this.viewEl = viewEl;   
24473         this.wrap.on('click', this.onClick,  this); 
24474         
24475         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24476         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24477         
24478         
24479         
24480         if(this.boxLabel){
24481             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24482         //    viewEl.on('click', this.onClick,  this); 
24483         }
24484         //if(this.checked){
24485             this.setChecked(this.checked);
24486         //}else{
24487             //this.checked = this.el.dom;
24488         //}
24489
24490     },
24491
24492     // private
24493     initValue : Roo.emptyFn,
24494
24495     /**
24496      * Returns the checked state of the checkbox.
24497      * @return {Boolean} True if checked, else false
24498      */
24499     getValue : function(){
24500         if(this.el){
24501             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24502         }
24503         return this.valueOff;
24504         
24505     },
24506
24507         // private
24508     onClick : function(){ 
24509         this.setChecked(!this.checked);
24510
24511         //if(this.el.dom.checked != this.checked){
24512         //    this.setValue(this.el.dom.checked);
24513        // }
24514     },
24515
24516     /**
24517      * Sets the checked state of the checkbox.
24518      * On is always based on a string comparison between inputValue and the param.
24519      * @param {Boolean/String} value - the value to set 
24520      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24521      */
24522     setValue : function(v,suppressEvent){
24523         
24524         
24525         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24526         //if(this.el && this.el.dom){
24527         //    this.el.dom.checked = this.checked;
24528         //    this.el.dom.defaultChecked = this.checked;
24529         //}
24530         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24531         //this.fireEvent("check", this, this.checked);
24532     },
24533     // private..
24534     setChecked : function(state,suppressEvent)
24535     {
24536         if (this.inSetChecked) {
24537             this.checked = state;
24538             return;
24539         }
24540         
24541     
24542         if(this.wrap){
24543             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24544         }
24545         this.checked = state;
24546         if(suppressEvent !== true){
24547             this.fireEvent('check', this, state);
24548         }
24549         this.inSetChecked = true;
24550         this.el.dom.value = state ? this.inputValue : this.valueOff;
24551         this.inSetChecked = false;
24552         
24553     },
24554     // handle setting of hidden value by some other method!!?!?
24555     setFromHidden: function()
24556     {
24557         if(!this.el){
24558             return;
24559         }
24560         //console.log("SET FROM HIDDEN");
24561         //alert('setFrom hidden');
24562         this.setValue(this.el.dom.value);
24563     },
24564     
24565     onDestroy : function()
24566     {
24567         if(this.viewEl){
24568             Roo.get(this.viewEl).remove();
24569         }
24570          
24571         Roo.form.Checkbox.superclass.onDestroy.call(this);
24572     }
24573
24574 });/*
24575  * Based on:
24576  * Ext JS Library 1.1.1
24577  * Copyright(c) 2006-2007, Ext JS, LLC.
24578  *
24579  * Originally Released Under LGPL - original licence link has changed is not relivant.
24580  *
24581  * Fork - LGPL
24582  * <script type="text/javascript">
24583  */
24584  
24585 /**
24586  * @class Roo.form.Radio
24587  * @extends Roo.form.Checkbox
24588  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24589  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24590  * @constructor
24591  * Creates a new Radio
24592  * @param {Object} config Configuration options
24593  */
24594 Roo.form.Radio = function(){
24595     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24596 };
24597 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24598     inputType: 'radio',
24599
24600     /**
24601      * If this radio is part of a group, it will return the selected value
24602      * @return {String}
24603      */
24604     getGroupValue : function(){
24605         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24606     }
24607 });//<script type="text/javascript">
24608
24609 /*
24610  * Ext JS Library 1.1.1
24611  * Copyright(c) 2006-2007, Ext JS, LLC.
24612  * licensing@extjs.com
24613  * 
24614  * http://www.extjs.com/license
24615  */
24616  
24617  /*
24618   * 
24619   * Known bugs:
24620   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
24621   * - IE ? - no idea how much works there.
24622   * 
24623   * 
24624   * 
24625   */
24626  
24627
24628 /**
24629  * @class Ext.form.HtmlEditor
24630  * @extends Ext.form.Field
24631  * Provides a lightweight HTML Editor component.
24632  *
24633  * This has been tested on Fireforx / Chrome.. IE may not be so great..
24634  * 
24635  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
24636  * supported by this editor.</b><br/><br/>
24637  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
24638  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24639  */
24640 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
24641       /**
24642      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24643      */
24644     toolbars : false,
24645     /**
24646      * @cfg {String} createLinkText The default text for the create link prompt
24647      */
24648     createLinkText : 'Please enter the URL for the link:',
24649     /**
24650      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
24651      */
24652     defaultLinkValue : 'http:/'+'/',
24653    
24654      /**
24655      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24656      *                        Roo.resizable.
24657      */
24658     resizable : false,
24659      /**
24660      * @cfg {Number} height (in pixels)
24661      */   
24662     height: 300,
24663    /**
24664      * @cfg {Number} width (in pixels)
24665      */   
24666     width: 500,
24667     
24668     /**
24669      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24670      * 
24671      */
24672     stylesheets: false,
24673     
24674     // id of frame..
24675     frameId: false,
24676     
24677     // private properties
24678     validationEvent : false,
24679     deferHeight: true,
24680     initialized : false,
24681     activated : false,
24682     sourceEditMode : false,
24683     onFocus : Roo.emptyFn,
24684     iframePad:3,
24685     hideMode:'offsets',
24686     
24687     defaultAutoCreate : { // modified by initCompnoent..
24688         tag: "textarea",
24689         style:"width:500px;height:300px;",
24690         autocomplete: "off"
24691     },
24692
24693     // private
24694     initComponent : function(){
24695         this.addEvents({
24696             /**
24697              * @event initialize
24698              * Fires when the editor is fully initialized (including the iframe)
24699              * @param {HtmlEditor} this
24700              */
24701             initialize: true,
24702             /**
24703              * @event activate
24704              * Fires when the editor is first receives the focus. Any insertion must wait
24705              * until after this event.
24706              * @param {HtmlEditor} this
24707              */
24708             activate: true,
24709              /**
24710              * @event beforesync
24711              * Fires before the textarea is updated with content from the editor iframe. Return false
24712              * to cancel the sync.
24713              * @param {HtmlEditor} this
24714              * @param {String} html
24715              */
24716             beforesync: true,
24717              /**
24718              * @event beforepush
24719              * Fires before the iframe editor is updated with content from the textarea. Return false
24720              * to cancel the push.
24721              * @param {HtmlEditor} this
24722              * @param {String} html
24723              */
24724             beforepush: true,
24725              /**
24726              * @event sync
24727              * Fires when the textarea is updated with content from the editor iframe.
24728              * @param {HtmlEditor} this
24729              * @param {String} html
24730              */
24731             sync: true,
24732              /**
24733              * @event push
24734              * Fires when the iframe editor is updated with content from the textarea.
24735              * @param {HtmlEditor} this
24736              * @param {String} html
24737              */
24738             push: true,
24739              /**
24740              * @event editmodechange
24741              * Fires when the editor switches edit modes
24742              * @param {HtmlEditor} this
24743              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24744              */
24745             editmodechange: true,
24746             /**
24747              * @event editorevent
24748              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24749              * @param {HtmlEditor} this
24750              */
24751             editorevent: true
24752         });
24753         this.defaultAutoCreate =  {
24754             tag: "textarea",
24755             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
24756             autocomplete: "off"
24757         };
24758     },
24759
24760     /**
24761      * Protected method that will not generally be called directly. It
24762      * is called when the editor creates its toolbar. Override this method if you need to
24763      * add custom toolbar buttons.
24764      * @param {HtmlEditor} editor
24765      */
24766     createToolbar : function(editor){
24767         if (!editor.toolbars || !editor.toolbars.length) {
24768             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
24769         }
24770         
24771         for (var i =0 ; i < editor.toolbars.length;i++) {
24772             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
24773             editor.toolbars[i].init(editor);
24774         }
24775          
24776         
24777     },
24778
24779     /**
24780      * Protected method that will not generally be called directly. It
24781      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24782      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24783      */
24784     getDocMarkup : function(){
24785         // body styles..
24786         var st = '';
24787         if (this.stylesheets === false) {
24788             
24789             Roo.get(document.head).select('style').each(function(node) {
24790                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24791             });
24792             
24793             Roo.get(document.head).select('link').each(function(node) { 
24794                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24795             });
24796             
24797         } else if (!this.stylesheets.length) {
24798                 // simple..
24799                 st = '<style type="text/css">' +
24800                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24801                    '</style>';
24802         } else {
24803             Roo.each(this.stylesheets, function(s) {
24804                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
24805             });
24806             
24807         }
24808         
24809         st +=  '<style type="text/css">' +
24810             'IMG { cursor: pointer } ' +
24811         '</style>';
24812
24813         
24814         return '<html><head>' + st  +
24815             //<style type="text/css">' +
24816             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24817             //'</style>' +
24818             ' </head><body class="roo-htmleditor-body"></body></html>';
24819     },
24820
24821     // private
24822     onRender : function(ct, position)
24823     {
24824         var _t = this;
24825         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24826         this.el.dom.style.border = '0 none';
24827         this.el.dom.setAttribute('tabIndex', -1);
24828         this.el.addClass('x-hidden');
24829         if(Roo.isIE){ // fix IE 1px bogus margin
24830             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24831         }
24832         this.wrap = this.el.wrap({
24833             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24834         });
24835         
24836         if (this.resizable) {
24837             this.resizeEl = new Roo.Resizable(this.wrap, {
24838                 pinned : true,
24839                 wrap: true,
24840                 dynamic : true,
24841                 minHeight : this.height,
24842                 height: this.height,
24843                 handles : this.resizable,
24844                 width: this.width,
24845                 listeners : {
24846                     resize : function(r, w, h) {
24847                         _t.onResize(w,h); // -something
24848                     }
24849                 }
24850             });
24851             
24852         }
24853
24854         this.frameId = Roo.id();
24855         
24856         this.createToolbar(this);
24857         
24858       
24859         
24860         var iframe = this.wrap.createChild({
24861             tag: 'iframe',
24862             id: this.frameId,
24863             name: this.frameId,
24864             frameBorder : 'no',
24865             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24866         }, this.el
24867         );
24868         
24869        // console.log(iframe);
24870         //this.wrap.dom.appendChild(iframe);
24871
24872         this.iframe = iframe.dom;
24873
24874          this.assignDocWin();
24875         
24876         this.doc.designMode = 'on';
24877        
24878         this.doc.open();
24879         this.doc.write(this.getDocMarkup());
24880         this.doc.close();
24881
24882         
24883         var task = { // must defer to wait for browser to be ready
24884             run : function(){
24885                 //console.log("run task?" + this.doc.readyState);
24886                 this.assignDocWin();
24887                 if(this.doc.body || this.doc.readyState == 'complete'){
24888                     try {
24889                         this.doc.designMode="on";
24890                     } catch (e) {
24891                         return;
24892                     }
24893                     Roo.TaskMgr.stop(task);
24894                     this.initEditor.defer(10, this);
24895                 }
24896             },
24897             interval : 10,
24898             duration:10000,
24899             scope: this
24900         };
24901         Roo.TaskMgr.start(task);
24902
24903         if(!this.width){
24904             this.setSize(this.wrap.getSize());
24905         }
24906         if (this.resizeEl) {
24907             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24908             // should trigger onReize..
24909         }
24910     },
24911
24912     // private
24913     onResize : function(w, h)
24914     {
24915         //Roo.log('resize: ' +w + ',' + h );
24916         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24917         if(this.el && this.iframe){
24918             if(typeof w == 'number'){
24919                 var aw = w - this.wrap.getFrameWidth('lr');
24920                 this.el.setWidth(this.adjustWidth('textarea', aw));
24921                 this.iframe.style.width = aw + 'px';
24922             }
24923             if(typeof h == 'number'){
24924                 var tbh = 0;
24925                 for (var i =0; i < this.toolbars.length;i++) {
24926                     // fixme - ask toolbars for heights?
24927                     tbh += this.toolbars[i].tb.el.getHeight();
24928                     if (this.toolbars[i].footer) {
24929                         tbh += this.toolbars[i].footer.el.getHeight();
24930                     }
24931                 }
24932                 
24933                 
24934                 
24935                 
24936                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24937                 ah -= 5; // knock a few pixes off for look..
24938                 this.el.setHeight(this.adjustWidth('textarea', ah));
24939                 this.iframe.style.height = ah + 'px';
24940                 if(this.doc){
24941                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24942                 }
24943             }
24944         }
24945     },
24946
24947     /**
24948      * Toggles the editor between standard and source edit mode.
24949      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24950      */
24951     toggleSourceEdit : function(sourceEditMode){
24952         
24953         this.sourceEditMode = sourceEditMode === true;
24954         
24955         if(this.sourceEditMode){
24956           
24957             this.syncValue();
24958             this.iframe.className = 'x-hidden';
24959             this.el.removeClass('x-hidden');
24960             this.el.dom.removeAttribute('tabIndex');
24961             this.el.focus();
24962         }else{
24963              
24964             this.pushValue();
24965             this.iframe.className = '';
24966             this.el.addClass('x-hidden');
24967             this.el.dom.setAttribute('tabIndex', -1);
24968             this.deferFocus();
24969         }
24970         this.setSize(this.wrap.getSize());
24971         this.fireEvent('editmodechange', this, this.sourceEditMode);
24972     },
24973
24974     // private used internally
24975     createLink : function(){
24976         var url = prompt(this.createLinkText, this.defaultLinkValue);
24977         if(url && url != 'http:/'+'/'){
24978             this.relayCmd('createlink', url);
24979         }
24980     },
24981
24982     // private (for BoxComponent)
24983     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24984
24985     // private (for BoxComponent)
24986     getResizeEl : function(){
24987         return this.wrap;
24988     },
24989
24990     // private (for BoxComponent)
24991     getPositionEl : function(){
24992         return this.wrap;
24993     },
24994
24995     // private
24996     initEvents : function(){
24997         this.originalValue = this.getValue();
24998     },
24999
25000     /**
25001      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25002      * @method
25003      */
25004     markInvalid : Roo.emptyFn,
25005     /**
25006      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25007      * @method
25008      */
25009     clearInvalid : Roo.emptyFn,
25010
25011     setValue : function(v){
25012         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25013         this.pushValue();
25014     },
25015
25016     /**
25017      * Protected method that will not generally be called directly. If you need/want
25018      * custom HTML cleanup, this is the method you should override.
25019      * @param {String} html The HTML to be cleaned
25020      * return {String} The cleaned HTML
25021      */
25022     cleanHtml : function(html){
25023         html = String(html);
25024         if(html.length > 5){
25025             if(Roo.isSafari){ // strip safari nonsense
25026                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25027             }
25028         }
25029         if(html == '&nbsp;'){
25030             html = '';
25031         }
25032         return html;
25033     },
25034
25035     /**
25036      * Protected method that will not generally be called directly. Syncs the contents
25037      * of the editor iframe with the textarea.
25038      */
25039     syncValue : function(){
25040         if(this.initialized){
25041             var bd = (this.doc.body || this.doc.documentElement);
25042             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25043             var html = bd.innerHTML;
25044             if(Roo.isSafari){
25045                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25046                 var m = bs.match(/text-align:(.*?);/i);
25047                 if(m && m[1]){
25048                     html = '<div style="'+m[0]+'">' + html + '</div>';
25049                 }
25050             }
25051             html = this.cleanHtml(html);
25052             // fix up the special chars..
25053             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25054                 return "&#"+b.charCodeAt()+";" 
25055             });
25056             if(this.fireEvent('beforesync', this, html) !== false){
25057                 this.el.dom.value = html;
25058                 this.fireEvent('sync', this, html);
25059             }
25060         }
25061     },
25062
25063     /**
25064      * Protected method that will not generally be called directly. Pushes the value of the textarea
25065      * into the iframe editor.
25066      */
25067     pushValue : function(){
25068         if(this.initialized){
25069             var v = this.el.dom.value;
25070             if(v.length < 1){
25071                 v = '&#160;';
25072             }
25073             
25074             if(this.fireEvent('beforepush', this, v) !== false){
25075                 var d = (this.doc.body || this.doc.documentElement);
25076                 d.innerHTML = v;
25077                 this.cleanUpPaste();
25078                 this.el.dom.value = d.innerHTML;
25079                 this.fireEvent('push', this, v);
25080             }
25081         }
25082     },
25083
25084     // private
25085     deferFocus : function(){
25086         this.focus.defer(10, this);
25087     },
25088
25089     // doc'ed in Field
25090     focus : function(){
25091         if(this.win && !this.sourceEditMode){
25092             this.win.focus();
25093         }else{
25094             this.el.focus();
25095         }
25096     },
25097     
25098     assignDocWin: function()
25099     {
25100         var iframe = this.iframe;
25101         
25102          if(Roo.isIE){
25103             this.doc = iframe.contentWindow.document;
25104             this.win = iframe.contentWindow;
25105         } else {
25106             if (!Roo.get(this.frameId)) {
25107                 return;
25108             }
25109             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25110             this.win = Roo.get(this.frameId).dom.contentWindow;
25111         }
25112     },
25113     
25114     // private
25115     initEditor : function(){
25116         //console.log("INIT EDITOR");
25117         this.assignDocWin();
25118         
25119         
25120         
25121         this.doc.designMode="on";
25122         this.doc.open();
25123         this.doc.write(this.getDocMarkup());
25124         this.doc.close();
25125         
25126         var dbody = (this.doc.body || this.doc.documentElement);
25127         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25128         // this copies styles from the containing element into thsi one..
25129         // not sure why we need all of this..
25130         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25131         ss['background-attachment'] = 'fixed'; // w3c
25132         dbody.bgProperties = 'fixed'; // ie
25133         Roo.DomHelper.applyStyles(dbody, ss);
25134         Roo.EventManager.on(this.doc, {
25135             //'mousedown': this.onEditorEvent,
25136             'mouseup': this.onEditorEvent,
25137             'dblclick': this.onEditorEvent,
25138             'click': this.onEditorEvent,
25139             'keyup': this.onEditorEvent,
25140             buffer:100,
25141             scope: this
25142         });
25143         if(Roo.isGecko){
25144             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25145         }
25146         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25147             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25148         }
25149         this.initialized = true;
25150
25151         this.fireEvent('initialize', this);
25152         this.pushValue();
25153     },
25154
25155     // private
25156     onDestroy : function(){
25157         
25158         
25159         
25160         if(this.rendered){
25161             
25162             for (var i =0; i < this.toolbars.length;i++) {
25163                 // fixme - ask toolbars for heights?
25164                 this.toolbars[i].onDestroy();
25165             }
25166             
25167             this.wrap.dom.innerHTML = '';
25168             this.wrap.remove();
25169         }
25170     },
25171
25172     // private
25173     onFirstFocus : function(){
25174         
25175         this.assignDocWin();
25176         
25177         
25178         this.activated = true;
25179         for (var i =0; i < this.toolbars.length;i++) {
25180             this.toolbars[i].onFirstFocus();
25181         }
25182        
25183         if(Roo.isGecko){ // prevent silly gecko errors
25184             this.win.focus();
25185             var s = this.win.getSelection();
25186             if(!s.focusNode || s.focusNode.nodeType != 3){
25187                 var r = s.getRangeAt(0);
25188                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25189                 r.collapse(true);
25190                 this.deferFocus();
25191             }
25192             try{
25193                 this.execCmd('useCSS', true);
25194                 this.execCmd('styleWithCSS', false);
25195             }catch(e){}
25196         }
25197         this.fireEvent('activate', this);
25198     },
25199
25200     // private
25201     adjustFont: function(btn){
25202         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25203         //if(Roo.isSafari){ // safari
25204         //    adjust *= 2;
25205        // }
25206         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25207         if(Roo.isSafari){ // safari
25208             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25209             v =  (v < 10) ? 10 : v;
25210             v =  (v > 48) ? 48 : v;
25211             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25212             
25213         }
25214         
25215         
25216         v = Math.max(1, v+adjust);
25217         
25218         this.execCmd('FontSize', v  );
25219     },
25220
25221     onEditorEvent : function(e){
25222         this.fireEvent('editorevent', this, e);
25223       //  this.updateToolbar();
25224         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25225     },
25226
25227     insertTag : function(tg)
25228     {
25229         // could be a bit smarter... -> wrap the current selected tRoo..
25230         
25231         this.execCmd("formatblock",   tg);
25232         
25233     },
25234     
25235     insertText : function(txt)
25236     {
25237         
25238         
25239         range = this.createRange();
25240         range.deleteContents();
25241                //alert(Sender.getAttribute('label'));
25242                
25243         range.insertNode(this.doc.createTextNode(txt));
25244     } ,
25245     
25246     // private
25247     relayBtnCmd : function(btn){
25248         this.relayCmd(btn.cmd);
25249     },
25250
25251     /**
25252      * Executes a Midas editor command on the editor document and performs necessary focus and
25253      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25254      * @param {String} cmd The Midas command
25255      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25256      */
25257     relayCmd : function(cmd, value){
25258         this.win.focus();
25259         this.execCmd(cmd, value);
25260         this.fireEvent('editorevent', this);
25261         //this.updateToolbar();
25262         this.deferFocus();
25263     },
25264
25265     /**
25266      * Executes a Midas editor command directly on the editor document.
25267      * For visual commands, you should use {@link #relayCmd} instead.
25268      * <b>This should only be called after the editor is initialized.</b>
25269      * @param {String} cmd The Midas command
25270      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25271      */
25272     execCmd : function(cmd, value){
25273         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25274         this.syncValue();
25275     },
25276  
25277  
25278    
25279     /**
25280      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25281      * to insert tRoo.
25282      * @param {String} text | dom node.. 
25283      */
25284     insertAtCursor : function(text)
25285     {
25286         
25287         
25288         
25289         if(!this.activated){
25290             return;
25291         }
25292         /*
25293         if(Roo.isIE){
25294             this.win.focus();
25295             var r = this.doc.selection.createRange();
25296             if(r){
25297                 r.collapse(true);
25298                 r.pasteHTML(text);
25299                 this.syncValue();
25300                 this.deferFocus();
25301             
25302             }
25303             return;
25304         }
25305         */
25306         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25307             this.win.focus();
25308             
25309             
25310             // from jquery ui (MIT licenced)
25311             var range, node;
25312             var win = this.win;
25313             
25314             if (win.getSelection && win.getSelection().getRangeAt) {
25315                 range = win.getSelection().getRangeAt(0);
25316                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25317                 range.insertNode(node);
25318             } else if (win.document.selection && win.document.selection.createRange) {
25319                 // no firefox support
25320                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25321                 win.document.selection.createRange().pasteHTML(txt);
25322             } else {
25323                 // no firefox support
25324                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25325                 this.execCmd('InsertHTML', txt);
25326             } 
25327             
25328             this.syncValue();
25329             
25330             this.deferFocus();
25331         }
25332     },
25333  // private
25334     mozKeyPress : function(e){
25335         if(e.ctrlKey){
25336             var c = e.getCharCode(), cmd;
25337           
25338             if(c > 0){
25339                 c = String.fromCharCode(c).toLowerCase();
25340                 switch(c){
25341                     case 'b':
25342                         cmd = 'bold';
25343                         break;
25344                     case 'i':
25345                         cmd = 'italic';
25346                         break;
25347                     
25348                     case 'u':
25349                         cmd = 'underline';
25350                         break;
25351                     
25352                     case 'v':
25353                         this.cleanUpPaste.defer(100, this);
25354                         return;
25355                         
25356                 }
25357                 if(cmd){
25358                     this.win.focus();
25359                     this.execCmd(cmd);
25360                     this.deferFocus();
25361                     e.preventDefault();
25362                 }
25363                 
25364             }
25365         }
25366     },
25367
25368     // private
25369     fixKeys : function(){ // load time branching for fastest keydown performance
25370         if(Roo.isIE){
25371             return function(e){
25372                 var k = e.getKey(), r;
25373                 if(k == e.TAB){
25374                     e.stopEvent();
25375                     r = this.doc.selection.createRange();
25376                     if(r){
25377                         r.collapse(true);
25378                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25379                         this.deferFocus();
25380                     }
25381                     return;
25382                 }
25383                 
25384                 if(k == e.ENTER){
25385                     r = this.doc.selection.createRange();
25386                     if(r){
25387                         var target = r.parentElement();
25388                         if(!target || target.tagName.toLowerCase() != 'li'){
25389                             e.stopEvent();
25390                             r.pasteHTML('<br />');
25391                             r.collapse(false);
25392                             r.select();
25393                         }
25394                     }
25395                 }
25396                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25397                     this.cleanUpPaste.defer(100, this);
25398                     return;
25399                 }
25400                 
25401                 
25402             };
25403         }else if(Roo.isOpera){
25404             return function(e){
25405                 var k = e.getKey();
25406                 if(k == e.TAB){
25407                     e.stopEvent();
25408                     this.win.focus();
25409                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25410                     this.deferFocus();
25411                 }
25412                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25413                     this.cleanUpPaste.defer(100, this);
25414                     return;
25415                 }
25416                 
25417             };
25418         }else if(Roo.isSafari){
25419             return function(e){
25420                 var k = e.getKey();
25421                 
25422                 if(k == e.TAB){
25423                     e.stopEvent();
25424                     this.execCmd('InsertText','\t');
25425                     this.deferFocus();
25426                     return;
25427                 }
25428                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25429                     this.cleanUpPaste.defer(100, this);
25430                     return;
25431                 }
25432                 
25433              };
25434         }
25435     }(),
25436     
25437     getAllAncestors: function()
25438     {
25439         var p = this.getSelectedNode();
25440         var a = [];
25441         if (!p) {
25442             a.push(p); // push blank onto stack..
25443             p = this.getParentElement();
25444         }
25445         
25446         
25447         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25448             a.push(p);
25449             p = p.parentNode;
25450         }
25451         a.push(this.doc.body);
25452         return a;
25453     },
25454     lastSel : false,
25455     lastSelNode : false,
25456     
25457     
25458     getSelection : function() 
25459     {
25460         this.assignDocWin();
25461         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25462     },
25463     
25464     getSelectedNode: function() 
25465     {
25466         // this may only work on Gecko!!!
25467         
25468         // should we cache this!!!!
25469         
25470         
25471         
25472          
25473         var range = this.createRange(this.getSelection()).cloneRange();
25474         
25475         if (Roo.isIE) {
25476             var parent = range.parentElement();
25477             while (true) {
25478                 var testRange = range.duplicate();
25479                 testRange.moveToElementText(parent);
25480                 if (testRange.inRange(range)) {
25481                     break;
25482                 }
25483                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25484                     break;
25485                 }
25486                 parent = parent.parentElement;
25487             }
25488             return parent;
25489         }
25490         
25491         // is ancestor a text element.
25492         var ac =  range.commonAncestorContainer;
25493         if (ac.nodeType == 3) {
25494             ac = ac.parentNode;
25495         }
25496         
25497         var ar = ac.childNodes;
25498          
25499         var nodes = [];
25500         var other_nodes = [];
25501         var has_other_nodes = false;
25502         for (var i=0;i<ar.length;i++) {
25503             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25504                 continue;
25505             }
25506             // fullly contained node.
25507             
25508             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25509                 nodes.push(ar[i]);
25510                 continue;
25511             }
25512             
25513             // probably selected..
25514             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25515                 other_nodes.push(ar[i]);
25516                 continue;
25517             }
25518             // outer..
25519             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25520                 continue;
25521             }
25522             
25523             
25524             has_other_nodes = true;
25525         }
25526         if (!nodes.length && other_nodes.length) {
25527             nodes= other_nodes;
25528         }
25529         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25530             return false;
25531         }
25532         
25533         return nodes[0];
25534     },
25535     createRange: function(sel)
25536     {
25537         // this has strange effects when using with 
25538         // top toolbar - not sure if it's a great idea.
25539         //this.editor.contentWindow.focus();
25540         if (typeof sel != "undefined") {
25541             try {
25542                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25543             } catch(e) {
25544                 return this.doc.createRange();
25545             }
25546         } else {
25547             return this.doc.createRange();
25548         }
25549     },
25550     getParentElement: function()
25551     {
25552         
25553         this.assignDocWin();
25554         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25555         
25556         var range = this.createRange(sel);
25557          
25558         try {
25559             var p = range.commonAncestorContainer;
25560             while (p.nodeType == 3) { // text node
25561                 p = p.parentNode;
25562             }
25563             return p;
25564         } catch (e) {
25565             return null;
25566         }
25567     
25568     },
25569     /***
25570      *
25571      * Range intersection.. the hard stuff...
25572      *  '-1' = before
25573      *  '0' = hits..
25574      *  '1' = after.
25575      *         [ -- selected range --- ]
25576      *   [fail]                        [fail]
25577      *
25578      *    basically..
25579      *      if end is before start or  hits it. fail.
25580      *      if start is after end or hits it fail.
25581      *
25582      *   if either hits (but other is outside. - then it's not 
25583      *   
25584      *    
25585      **/
25586     
25587     
25588     // @see http://www.thismuchiknow.co.uk/?p=64.
25589     rangeIntersectsNode : function(range, node)
25590     {
25591         var nodeRange = node.ownerDocument.createRange();
25592         try {
25593             nodeRange.selectNode(node);
25594         } catch (e) {
25595             nodeRange.selectNodeContents(node);
25596         }
25597     
25598         var rangeStartRange = range.cloneRange();
25599         rangeStartRange.collapse(true);
25600     
25601         var rangeEndRange = range.cloneRange();
25602         rangeEndRange.collapse(false);
25603     
25604         var nodeStartRange = nodeRange.cloneRange();
25605         nodeStartRange.collapse(true);
25606     
25607         var nodeEndRange = nodeRange.cloneRange();
25608         nodeEndRange.collapse(false);
25609     
25610         return rangeStartRange.compareBoundaryPoints(
25611                  Range.START_TO_START, nodeEndRange) == -1 &&
25612                rangeEndRange.compareBoundaryPoints(
25613                  Range.START_TO_START, nodeStartRange) == 1;
25614         
25615          
25616     },
25617     rangeCompareNode : function(range, node)
25618     {
25619         var nodeRange = node.ownerDocument.createRange();
25620         try {
25621             nodeRange.selectNode(node);
25622         } catch (e) {
25623             nodeRange.selectNodeContents(node);
25624         }
25625         
25626         
25627         range.collapse(true);
25628     
25629         nodeRange.collapse(true);
25630      
25631         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25632         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25633          
25634         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25635         
25636         var nodeIsBefore   =  ss == 1;
25637         var nodeIsAfter    = ee == -1;
25638         
25639         if (nodeIsBefore && nodeIsAfter)
25640             return 0; // outer
25641         if (!nodeIsBefore && nodeIsAfter)
25642             return 1; //right trailed.
25643         
25644         if (nodeIsBefore && !nodeIsAfter)
25645             return 2;  // left trailed.
25646         // fully contined.
25647         return 3;
25648     },
25649
25650     // private? - in a new class?
25651     cleanUpPaste :  function()
25652     {
25653         // cleans up the whole document..
25654          Roo.log('cleanuppaste');
25655         this.cleanUpChildren(this.doc.body);
25656         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25657         if (clean != this.doc.body.innerHTML) {
25658             this.doc.body.innerHTML = clean;
25659         }
25660         
25661     },
25662     
25663     cleanWordChars : function(input) {
25664         var he = Roo.form.HtmlEditor;
25665     
25666         var output = input;
25667         Roo.each(he.swapCodes, function(sw) { 
25668         
25669             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25670             output = output.replace(swapper, sw[1]);
25671         });
25672         return output;
25673     },
25674     
25675     
25676     cleanUpChildren : function (n)
25677     {
25678         if (!n.childNodes.length) {
25679             return;
25680         }
25681         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25682            this.cleanUpChild(n.childNodes[i]);
25683         }
25684     },
25685     
25686     
25687         
25688     
25689     cleanUpChild : function (node)
25690     {
25691         //console.log(node);
25692         if (node.nodeName == "#text") {
25693             // clean up silly Windows -- stuff?
25694             return; 
25695         }
25696         if (node.nodeName == "#comment") {
25697             node.parentNode.removeChild(node);
25698             // clean up silly Windows -- stuff?
25699             return; 
25700         }
25701         
25702         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
25703             // remove node.
25704             node.parentNode.removeChild(node);
25705             return;
25706             
25707         }
25708         
25709         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
25710         
25711         // remove <a name=....> as rendering on yahoo mailer is bored with this.
25712         
25713         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25714             remove_keep_children = true;
25715         }
25716         
25717         if (remove_keep_children) {
25718             this.cleanUpChildren(node);
25719             // inserts everything just before this node...
25720             while (node.childNodes.length) {
25721                 var cn = node.childNodes[0];
25722                 node.removeChild(cn);
25723                 node.parentNode.insertBefore(cn, node);
25724             }
25725             node.parentNode.removeChild(node);
25726             return;
25727         }
25728         
25729         if (!node.attributes || !node.attributes.length) {
25730             this.cleanUpChildren(node);
25731             return;
25732         }
25733         
25734         function cleanAttr(n,v)
25735         {
25736             
25737             if (v.match(/^\./) || v.match(/^\//)) {
25738                 return;
25739             }
25740             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25741                 return;
25742             }
25743             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
25744             node.removeAttribute(n);
25745             
25746         }
25747         
25748         function cleanStyle(n,v)
25749         {
25750             if (v.match(/expression/)) { //XSS?? should we even bother..
25751                 node.removeAttribute(n);
25752                 return;
25753             }
25754             
25755             
25756             var parts = v.split(/;/);
25757             Roo.each(parts, function(p) {
25758                 p = p.replace(/\s+/g,'');
25759                 if (!p.length) {
25760                     return true;
25761                 }
25762                 var l = p.split(':').shift().replace(/\s+/g,'');
25763                 
25764                 // only allow 'c whitelisted system attributes'
25765                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
25766                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
25767                     node.removeAttribute(n);
25768                     return false;
25769                 }
25770                 return true;
25771             });
25772             
25773             
25774         }
25775         
25776         
25777         for (var i = node.attributes.length-1; i > -1 ; i--) {
25778             var a = node.attributes[i];
25779             //console.log(a);
25780             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
25781                 node.removeAttribute(a.name);
25782                 return;
25783             }
25784             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
25785                 cleanAttr(a.name,a.value); // fixme..
25786                 return;
25787             }
25788             if (a.name == 'style') {
25789                 cleanStyle(a.name,a.value);
25790             }
25791             /// clean up MS crap..
25792             // tecnically this should be a list of valid class'es..
25793             
25794             
25795             if (a.name == 'class') {
25796                 if (a.value.match(/^Mso/)) {
25797                     node.className = '';
25798                 }
25799                 
25800                 if (a.value.match(/body/)) {
25801                     node.className = '';
25802                 }
25803             }
25804             
25805             // style cleanup!?
25806             // class cleanup?
25807             
25808         }
25809         
25810         
25811         this.cleanUpChildren(node);
25812         
25813         
25814     }
25815     
25816     
25817     // hide stuff that is not compatible
25818     /**
25819      * @event blur
25820      * @hide
25821      */
25822     /**
25823      * @event change
25824      * @hide
25825      */
25826     /**
25827      * @event focus
25828      * @hide
25829      */
25830     /**
25831      * @event specialkey
25832      * @hide
25833      */
25834     /**
25835      * @cfg {String} fieldClass @hide
25836      */
25837     /**
25838      * @cfg {String} focusClass @hide
25839      */
25840     /**
25841      * @cfg {String} autoCreate @hide
25842      */
25843     /**
25844      * @cfg {String} inputType @hide
25845      */
25846     /**
25847      * @cfg {String} invalidClass @hide
25848      */
25849     /**
25850      * @cfg {String} invalidText @hide
25851      */
25852     /**
25853      * @cfg {String} msgFx @hide
25854      */
25855     /**
25856      * @cfg {String} validateOnBlur @hide
25857      */
25858 });
25859
25860 Roo.form.HtmlEditor.white = [
25861         'area', 'br', 'img', 'input', 'hr', 'wbr',
25862         
25863        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25864        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25865        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25866        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25867        'table',   'ul',         'xmp', 
25868        
25869        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25870       'thead',   'tr', 
25871      
25872       'dir', 'menu', 'ol', 'ul', 'dl',
25873        
25874       'embed',  'object'
25875 ];
25876
25877
25878 Roo.form.HtmlEditor.black = [
25879     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25880         'applet', // 
25881         'base',   'basefont', 'bgsound', 'blink',  'body', 
25882         'frame',  'frameset', 'head',    'html',   'ilayer', 
25883         'iframe', 'layer',  'link',     'meta',    'object',   
25884         'script', 'style' ,'title',  'xml' // clean later..
25885 ];
25886 Roo.form.HtmlEditor.clean = [
25887     'script', 'style', 'title', 'xml'
25888 ];
25889 Roo.form.HtmlEditor.remove = [
25890     'font'
25891 ];
25892 // attributes..
25893
25894 Roo.form.HtmlEditor.ablack = [
25895     'on'
25896 ];
25897     
25898 Roo.form.HtmlEditor.aclean = [ 
25899     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25900 ];
25901
25902 // protocols..
25903 Roo.form.HtmlEditor.pwhite= [
25904         'http',  'https',  'mailto'
25905 ];
25906
25907 // white listed style attributes.
25908 Roo.form.HtmlEditor.cwhite= [
25909         'text-align',
25910         'font-size'
25911 ];
25912
25913
25914 Roo.form.HtmlEditor.swapCodes   =[ 
25915     [    8211, "--" ], 
25916     [    8212, "--" ], 
25917     [    8216,  "'" ],  
25918     [    8217, "'" ],  
25919     [    8220, '"' ],  
25920     [    8221, '"' ],  
25921     [    8226, "*" ],  
25922     [    8230, "..." ]
25923 ]; 
25924
25925     // <script type="text/javascript">
25926 /*
25927  * Based on
25928  * Ext JS Library 1.1.1
25929  * Copyright(c) 2006-2007, Ext JS, LLC.
25930  *  
25931  
25932  */
25933
25934 /**
25935  * @class Roo.form.HtmlEditorToolbar1
25936  * Basic Toolbar
25937  * 
25938  * Usage:
25939  *
25940  new Roo.form.HtmlEditor({
25941     ....
25942     toolbars : [
25943         new Roo.form.HtmlEditorToolbar1({
25944             disable : { fonts: 1 , format: 1, ..., ... , ...],
25945             btns : [ .... ]
25946         })
25947     }
25948      
25949  * 
25950  * @cfg {Object} disable List of elements to disable..
25951  * @cfg {Array} btns List of additional buttons.
25952  * 
25953  * 
25954  * NEEDS Extra CSS? 
25955  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25956  */
25957  
25958 Roo.form.HtmlEditor.ToolbarStandard = function(config)
25959 {
25960     
25961     Roo.apply(this, config);
25962     
25963     // default disabled, based on 'good practice'..
25964     this.disable = this.disable || {};
25965     Roo.applyIf(this.disable, {
25966         fontSize : true,
25967         colors : true,
25968         specialElements : true
25969     });
25970     
25971     
25972     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25973     // dont call parent... till later.
25974 }
25975
25976 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
25977     
25978     tb: false,
25979     
25980     rendered: false,
25981     
25982     editor : false,
25983     /**
25984      * @cfg {Object} disable  List of toolbar elements to disable
25985          
25986      */
25987     disable : false,
25988       /**
25989      * @cfg {Array} fontFamilies An array of available font families
25990      */
25991     fontFamilies : [
25992         'Arial',
25993         'Courier New',
25994         'Tahoma',
25995         'Times New Roman',
25996         'Verdana'
25997     ],
25998     
25999     specialChars : [
26000            "&#169;",
26001           "&#174;",     
26002           "&#8482;",    
26003           "&#163;" ,    
26004          // "&#8212;",    
26005           "&#8230;",    
26006           "&#247;" ,    
26007         //  "&#225;" ,     ?? a acute?
26008            "&#8364;"    , //Euro
26009        //   "&#8220;"    ,
26010         //  "&#8221;"    ,
26011         //  "&#8226;"    ,
26012           "&#176;"  //   , // degrees
26013
26014          // "&#233;"     , // e ecute
26015          // "&#250;"     , // u ecute?
26016     ],
26017     
26018     specialElements : [
26019         {
26020             text: "Insert Table",
26021             xtype: 'MenuItem',
26022             xns : Roo.Menu,
26023             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26024                 
26025         },
26026         {    
26027             text: "Insert Image",
26028             xtype: 'MenuItem',
26029             xns : Roo.Menu,
26030             ihtml : '<img src="about:blank"/>'
26031             
26032         }
26033         
26034          
26035     ],
26036     
26037     
26038     inputElements : [ 
26039             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26040             "input:submit", "input:button", "select", "textarea", "label" ],
26041     formats : [
26042         ["p"] ,  
26043         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26044         ["pre"],[ "code"], 
26045         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
26046     ],
26047      /**
26048      * @cfg {String} defaultFont default font to use.
26049      */
26050     defaultFont: 'tahoma',
26051    
26052     fontSelect : false,
26053     
26054     
26055     formatCombo : false,
26056     
26057     init : function(editor)
26058     {
26059         this.editor = editor;
26060         
26061         
26062         var fid = editor.frameId;
26063         var etb = this;
26064         function btn(id, toggle, handler){
26065             var xid = fid + '-'+ id ;
26066             return {
26067                 id : xid,
26068                 cmd : id,
26069                 cls : 'x-btn-icon x-edit-'+id,
26070                 enableToggle:toggle !== false,
26071                 scope: editor, // was editor...
26072                 handler:handler||editor.relayBtnCmd,
26073                 clickEvent:'mousedown',
26074                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26075                 tabIndex:-1
26076             };
26077         }
26078         
26079         
26080         
26081         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26082         this.tb = tb;
26083          // stop form submits
26084         tb.el.on('click', function(e){
26085             e.preventDefault(); // what does this do?
26086         });
26087
26088         if(!this.disable.font && !Roo.isSafari){
26089             /* why no safari for fonts
26090             editor.fontSelect = tb.el.createChild({
26091                 tag:'select',
26092                 tabIndex: -1,
26093                 cls:'x-font-select',
26094                 html: editor.createFontOptions()
26095             });
26096             editor.fontSelect.on('change', function(){
26097                 var font = editor.fontSelect.dom.value;
26098                 editor.relayCmd('fontname', font);
26099                 editor.deferFocus();
26100             }, editor);
26101             tb.add(
26102                 editor.fontSelect.dom,
26103                 '-'
26104             );
26105             */
26106         };
26107         if(!this.disable.formats){
26108             this.formatCombo = new Roo.form.ComboBox({
26109                 store: new Roo.data.SimpleStore({
26110                     id : 'tag',
26111                     fields: ['tag'],
26112                     data : this.formats // from states.js
26113                 }),
26114                 blockFocus : true,
26115                 //autoCreate : {tag: "div",  size: "20"},
26116                 displayField:'tag',
26117                 typeAhead: false,
26118                 mode: 'local',
26119                 editable : false,
26120                 triggerAction: 'all',
26121                 emptyText:'Add tag',
26122                 selectOnFocus:true,
26123                 width:135,
26124                 listeners : {
26125                     'select': function(c, r, i) {
26126                         editor.insertTag(r.get('tag'));
26127                         editor.focus();
26128                     }
26129                 }
26130
26131             });
26132             tb.addField(this.formatCombo);
26133             
26134         }
26135         
26136         if(!this.disable.format){
26137             tb.add(
26138                 btn('bold'),
26139                 btn('italic'),
26140                 btn('underline')
26141             );
26142         };
26143         if(!this.disable.fontSize){
26144             tb.add(
26145                 '-',
26146                 
26147                 
26148                 btn('increasefontsize', false, editor.adjustFont),
26149                 btn('decreasefontsize', false, editor.adjustFont)
26150             );
26151         };
26152         
26153         
26154         if(!this.disable.colors){
26155             tb.add(
26156                 '-', {
26157                     id:editor.frameId +'-forecolor',
26158                     cls:'x-btn-icon x-edit-forecolor',
26159                     clickEvent:'mousedown',
26160                     tooltip: this.buttonTips['forecolor'] || undefined,
26161                     tabIndex:-1,
26162                     menu : new Roo.menu.ColorMenu({
26163                         allowReselect: true,
26164                         focus: Roo.emptyFn,
26165                         value:'000000',
26166                         plain:true,
26167                         selectHandler: function(cp, color){
26168                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26169                             editor.deferFocus();
26170                         },
26171                         scope: editor,
26172                         clickEvent:'mousedown'
26173                     })
26174                 }, {
26175                     id:editor.frameId +'backcolor',
26176                     cls:'x-btn-icon x-edit-backcolor',
26177                     clickEvent:'mousedown',
26178                     tooltip: this.buttonTips['backcolor'] || undefined,
26179                     tabIndex:-1,
26180                     menu : new Roo.menu.ColorMenu({
26181                         focus: Roo.emptyFn,
26182                         value:'FFFFFF',
26183                         plain:true,
26184                         allowReselect: true,
26185                         selectHandler: function(cp, color){
26186                             if(Roo.isGecko){
26187                                 editor.execCmd('useCSS', false);
26188                                 editor.execCmd('hilitecolor', color);
26189                                 editor.execCmd('useCSS', true);
26190                                 editor.deferFocus();
26191                             }else{
26192                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26193                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26194                                 editor.deferFocus();
26195                             }
26196                         },
26197                         scope:editor,
26198                         clickEvent:'mousedown'
26199                     })
26200                 }
26201             );
26202         };
26203         // now add all the items...
26204         
26205
26206         if(!this.disable.alignments){
26207             tb.add(
26208                 '-',
26209                 btn('justifyleft'),
26210                 btn('justifycenter'),
26211                 btn('justifyright')
26212             );
26213         };
26214
26215         //if(!Roo.isSafari){
26216             if(!this.disable.links){
26217                 tb.add(
26218                     '-',
26219                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
26220                 );
26221             };
26222
26223             if(!this.disable.lists){
26224                 tb.add(
26225                     '-',
26226                     btn('insertorderedlist'),
26227                     btn('insertunorderedlist')
26228                 );
26229             }
26230             if(!this.disable.sourceEdit){
26231                 tb.add(
26232                     '-',
26233                     btn('sourceedit', true, function(btn){
26234                         this.toggleSourceEdit(btn.pressed);
26235                     })
26236                 );
26237             }
26238         //}
26239         
26240         var smenu = { };
26241         // special menu.. - needs to be tidied up..
26242         if (!this.disable.special) {
26243             smenu = {
26244                 text: "&#169;",
26245                 cls: 'x-edit-none',
26246                 
26247                 menu : {
26248                     items : []
26249                 }
26250             };
26251             for (var i =0; i < this.specialChars.length; i++) {
26252                 smenu.menu.items.push({
26253                     
26254                     html: this.specialChars[i],
26255                     handler: function(a,b) {
26256                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26257                         //editor.insertAtCursor(a.html);
26258                         
26259                     },
26260                     tabIndex:-1
26261                 });
26262             }
26263             
26264             
26265             tb.add(smenu);
26266             
26267             
26268         }
26269          
26270         if (!this.disable.specialElements) {
26271             var semenu = {
26272                 text: "Other;",
26273                 cls: 'x-edit-none',
26274                 menu : {
26275                     items : []
26276                 }
26277             };
26278             for (var i =0; i < this.specialElements.length; i++) {
26279                 semenu.menu.items.push(
26280                     Roo.apply({ 
26281                         handler: function(a,b) {
26282                             editor.insertAtCursor(this.ihtml);
26283                         }
26284                     }, this.specialElements[i])
26285                 );
26286                     
26287             }
26288             
26289             tb.add(semenu);
26290             
26291             
26292         }
26293          
26294         
26295         if (this.btns) {
26296             for(var i =0; i< this.btns.length;i++) {
26297                 var b = Roo.factory(this.btns[i],Roo.form);
26298                 b.cls =  'x-edit-none';
26299                 b.scope = editor;
26300                 tb.add(b);
26301             }
26302         
26303         }
26304         
26305         
26306         
26307         // disable everything...
26308         
26309         this.tb.items.each(function(item){
26310            if(item.id != editor.frameId+ '-sourceedit'){
26311                 item.disable();
26312             }
26313         });
26314         this.rendered = true;
26315         
26316         // the all the btns;
26317         editor.on('editorevent', this.updateToolbar, this);
26318         // other toolbars need to implement this..
26319         //editor.on('editmodechange', this.updateToolbar, this);
26320     },
26321     
26322     
26323     
26324     /**
26325      * Protected method that will not generally be called directly. It triggers
26326      * a toolbar update by reading the markup state of the current selection in the editor.
26327      */
26328     updateToolbar: function(){
26329
26330         if(!this.editor.activated){
26331             this.editor.onFirstFocus();
26332             return;
26333         }
26334
26335         var btns = this.tb.items.map, 
26336             doc = this.editor.doc,
26337             frameId = this.editor.frameId;
26338
26339         if(!this.disable.font && !Roo.isSafari){
26340             /*
26341             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
26342             if(name != this.fontSelect.dom.value){
26343                 this.fontSelect.dom.value = name;
26344             }
26345             */
26346         }
26347         if(!this.disable.format){
26348             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
26349             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
26350             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
26351         }
26352         if(!this.disable.alignments){
26353             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
26354             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
26355             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
26356         }
26357         if(!Roo.isSafari && !this.disable.lists){
26358             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
26359             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
26360         }
26361         
26362         var ans = this.editor.getAllAncestors();
26363         if (this.formatCombo) {
26364             
26365             
26366             var store = this.formatCombo.store;
26367             this.formatCombo.setValue("");
26368             for (var i =0; i < ans.length;i++) {
26369                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26370                     // select it..
26371                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26372                     break;
26373                 }
26374             }
26375         }
26376         
26377         
26378         
26379         // hides menus... - so this cant be on a menu...
26380         Roo.menu.MenuMgr.hideAll();
26381
26382         //this.editorsyncValue();
26383     },
26384    
26385     
26386     createFontOptions : function(){
26387         var buf = [], fs = this.fontFamilies, ff, lc;
26388         for(var i = 0, len = fs.length; i< len; i++){
26389             ff = fs[i];
26390             lc = ff.toLowerCase();
26391             buf.push(
26392                 '<option value="',lc,'" style="font-family:',ff,';"',
26393                     (this.defaultFont == lc ? ' selected="true">' : '>'),
26394                     ff,
26395                 '</option>'
26396             );
26397         }
26398         return buf.join('');
26399     },
26400     
26401     toggleSourceEdit : function(sourceEditMode){
26402         if(sourceEditMode === undefined){
26403             sourceEditMode = !this.sourceEditMode;
26404         }
26405         this.sourceEditMode = sourceEditMode === true;
26406         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
26407         // just toggle the button?
26408         if(btn.pressed !== this.editor.sourceEditMode){
26409             btn.toggle(this.editor.sourceEditMode);
26410             return;
26411         }
26412         
26413         if(this.sourceEditMode){
26414             this.tb.items.each(function(item){
26415                 if(item.cmd != 'sourceedit'){
26416                     item.disable();
26417                 }
26418             });
26419           
26420         }else{
26421             if(this.initialized){
26422                 this.tb.items.each(function(item){
26423                     item.enable();
26424                 });
26425             }
26426             
26427         }
26428         // tell the editor that it's been pressed..
26429         this.editor.toggleSourceEdit(sourceEditMode);
26430        
26431     },
26432      /**
26433      * Object collection of toolbar tooltips for the buttons in the editor. The key
26434      * is the command id associated with that button and the value is a valid QuickTips object.
26435      * For example:
26436 <pre><code>
26437 {
26438     bold : {
26439         title: 'Bold (Ctrl+B)',
26440         text: 'Make the selected text bold.',
26441         cls: 'x-html-editor-tip'
26442     },
26443     italic : {
26444         title: 'Italic (Ctrl+I)',
26445         text: 'Make the selected text italic.',
26446         cls: 'x-html-editor-tip'
26447     },
26448     ...
26449 </code></pre>
26450     * @type Object
26451      */
26452     buttonTips : {
26453         bold : {
26454             title: 'Bold (Ctrl+B)',
26455             text: 'Make the selected text bold.',
26456             cls: 'x-html-editor-tip'
26457         },
26458         italic : {
26459             title: 'Italic (Ctrl+I)',
26460             text: 'Make the selected text italic.',
26461             cls: 'x-html-editor-tip'
26462         },
26463         underline : {
26464             title: 'Underline (Ctrl+U)',
26465             text: 'Underline the selected text.',
26466             cls: 'x-html-editor-tip'
26467         },
26468         increasefontsize : {
26469             title: 'Grow Text',
26470             text: 'Increase the font size.',
26471             cls: 'x-html-editor-tip'
26472         },
26473         decreasefontsize : {
26474             title: 'Shrink Text',
26475             text: 'Decrease the font size.',
26476             cls: 'x-html-editor-tip'
26477         },
26478         backcolor : {
26479             title: 'Text Highlight Color',
26480             text: 'Change the background color of the selected text.',
26481             cls: 'x-html-editor-tip'
26482         },
26483         forecolor : {
26484             title: 'Font Color',
26485             text: 'Change the color of the selected text.',
26486             cls: 'x-html-editor-tip'
26487         },
26488         justifyleft : {
26489             title: 'Align Text Left',
26490             text: 'Align text to the left.',
26491             cls: 'x-html-editor-tip'
26492         },
26493         justifycenter : {
26494             title: 'Center Text',
26495             text: 'Center text in the editor.',
26496             cls: 'x-html-editor-tip'
26497         },
26498         justifyright : {
26499             title: 'Align Text Right',
26500             text: 'Align text to the right.',
26501             cls: 'x-html-editor-tip'
26502         },
26503         insertunorderedlist : {
26504             title: 'Bullet List',
26505             text: 'Start a bulleted list.',
26506             cls: 'x-html-editor-tip'
26507         },
26508         insertorderedlist : {
26509             title: 'Numbered List',
26510             text: 'Start a numbered list.',
26511             cls: 'x-html-editor-tip'
26512         },
26513         createlink : {
26514             title: 'Hyperlink',
26515             text: 'Make the selected text a hyperlink.',
26516             cls: 'x-html-editor-tip'
26517         },
26518         sourceedit : {
26519             title: 'Source Edit',
26520             text: 'Switch to source editing mode.',
26521             cls: 'x-html-editor-tip'
26522         }
26523     },
26524     // private
26525     onDestroy : function(){
26526         if(this.rendered){
26527             
26528             this.tb.items.each(function(item){
26529                 if(item.menu){
26530                     item.menu.removeAll();
26531                     if(item.menu.el){
26532                         item.menu.el.destroy();
26533                     }
26534                 }
26535                 item.destroy();
26536             });
26537              
26538         }
26539     },
26540     onFirstFocus: function() {
26541         this.tb.items.each(function(item){
26542            item.enable();
26543         });
26544     }
26545 });
26546
26547
26548
26549
26550 // <script type="text/javascript">
26551 /*
26552  * Based on
26553  * Ext JS Library 1.1.1
26554  * Copyright(c) 2006-2007, Ext JS, LLC.
26555  *  
26556  
26557  */
26558
26559  
26560 /**
26561  * @class Roo.form.HtmlEditor.ToolbarContext
26562  * Context Toolbar
26563  * 
26564  * Usage:
26565  *
26566  new Roo.form.HtmlEditor({
26567     ....
26568     toolbars : [
26569         { xtype: 'ToolbarStandard', styles : {} }
26570         { xtype: 'ToolbarContext', disable : {} }
26571     ]
26572 })
26573
26574      
26575  * 
26576  * @config : {Object} disable List of elements to disable.. (not done yet.)
26577  * @config : {Object} styles  Map of styles available.
26578  * 
26579  */
26580
26581 Roo.form.HtmlEditor.ToolbarContext = function(config)
26582 {
26583     
26584     Roo.apply(this, config);
26585     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26586     // dont call parent... till later.
26587     this.styles = this.styles || {};
26588 }
26589 Roo.form.HtmlEditor.ToolbarContext.types = {
26590     'IMG' : {
26591         width : {
26592             title: "Width",
26593             width: 40
26594         },
26595         height:  {
26596             title: "Height",
26597             width: 40
26598         },
26599         align: {
26600             title: "Align",
26601             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
26602             width : 80
26603             
26604         },
26605         border: {
26606             title: "Border",
26607             width: 40
26608         },
26609         alt: {
26610             title: "Alt",
26611             width: 120
26612         },
26613         src : {
26614             title: "Src",
26615             width: 220
26616         }
26617         
26618     },
26619     'A' : {
26620         name : {
26621             title: "Name",
26622             width: 50
26623         },
26624         href:  {
26625             title: "Href",
26626             width: 220
26627         } // border?
26628         
26629     },
26630     'TABLE' : {
26631         rows : {
26632             title: "Rows",
26633             width: 20
26634         },
26635         cols : {
26636             title: "Cols",
26637             width: 20
26638         },
26639         width : {
26640             title: "Width",
26641             width: 40
26642         },
26643         height : {
26644             title: "Height",
26645             width: 40
26646         },
26647         border : {
26648             title: "Border",
26649             width: 20
26650         }
26651     },
26652     'TD' : {
26653         width : {
26654             title: "Width",
26655             width: 40
26656         },
26657         height : {
26658             title: "Height",
26659             width: 40
26660         },   
26661         align: {
26662             title: "Align",
26663             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
26664             width: 80
26665         },
26666         valign: {
26667             title: "Valign",
26668             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
26669             width: 80
26670         },
26671         colspan: {
26672             title: "Colspan",
26673             width: 20
26674             
26675         }
26676     },
26677     'INPUT' : {
26678         name : {
26679             title: "name",
26680             width: 120
26681         },
26682         value : {
26683             title: "Value",
26684             width: 120
26685         },
26686         width : {
26687             title: "Width",
26688             width: 40
26689         }
26690     },
26691     'LABEL' : {
26692         'for' : {
26693             title: "For",
26694             width: 120
26695         }
26696     },
26697     'TEXTAREA' : {
26698           name : {
26699             title: "name",
26700             width: 120
26701         },
26702         rows : {
26703             title: "Rows",
26704             width: 20
26705         },
26706         cols : {
26707             title: "Cols",
26708             width: 20
26709         }
26710     },
26711     'SELECT' : {
26712         name : {
26713             title: "name",
26714             width: 120
26715         },
26716         selectoptions : {
26717             title: "Options",
26718             width: 200
26719         }
26720     },
26721     
26722     // should we really allow this??
26723     // should this just be 
26724     'BODY' : {
26725         title : {
26726             title: "title",
26727             width: 200,
26728             disabled : true
26729         }
26730     },
26731     '*' : {
26732         // empty..
26733     }
26734 };
26735
26736
26737
26738 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
26739     
26740     tb: false,
26741     
26742     rendered: false,
26743     
26744     editor : false,
26745     /**
26746      * @cfg {Object} disable  List of toolbar elements to disable
26747          
26748      */
26749     disable : false,
26750     /**
26751      * @cfg {Object} styles List of styles 
26752      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
26753      *
26754      * These must be defined in the page, so they get rendered correctly..
26755      * .headline { }
26756      * TD.underline { }
26757      * 
26758      */
26759     styles : false,
26760     
26761     
26762     
26763     toolbars : false,
26764     
26765     init : function(editor)
26766     {
26767         this.editor = editor;
26768         
26769         
26770         var fid = editor.frameId;
26771         var etb = this;
26772         function btn(id, toggle, handler){
26773             var xid = fid + '-'+ id ;
26774             return {
26775                 id : xid,
26776                 cmd : id,
26777                 cls : 'x-btn-icon x-edit-'+id,
26778                 enableToggle:toggle !== false,
26779                 scope: editor, // was editor...
26780                 handler:handler||editor.relayBtnCmd,
26781                 clickEvent:'mousedown',
26782                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26783                 tabIndex:-1
26784             };
26785         }
26786         // create a new element.
26787         var wdiv = editor.wrap.createChild({
26788                 tag: 'div'
26789             }, editor.wrap.dom.firstChild.nextSibling, true);
26790         
26791         // can we do this more than once??
26792         
26793          // stop form submits
26794       
26795  
26796         // disable everything...
26797         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26798         this.toolbars = {};
26799            
26800         for (var i in  ty) {
26801           
26802             this.toolbars[i] = this.buildToolbar(ty[i],i);
26803         }
26804         this.tb = this.toolbars.BODY;
26805         this.tb.el.show();
26806         this.buildFooter();
26807         this.footer.show();
26808         editor.on('hide', function( ) { this.footer.hide() }, this);
26809         editor.on('show', function( ) { this.footer.show() }, this);
26810         
26811          
26812         this.rendered = true;
26813         
26814         // the all the btns;
26815         editor.on('editorevent', this.updateToolbar, this);
26816         // other toolbars need to implement this..
26817         //editor.on('editmodechange', this.updateToolbar, this);
26818     },
26819     
26820     
26821     
26822     /**
26823      * Protected method that will not generally be called directly. It triggers
26824      * a toolbar update by reading the markup state of the current selection in the editor.
26825      */
26826     updateToolbar: function(editor,ev,sel){
26827
26828         //Roo.log(ev);
26829         // capture mouse up - this is handy for selecting images..
26830         // perhaps should go somewhere else...
26831         if(!this.editor.activated){
26832              this.editor.onFirstFocus();
26833             return;
26834         }
26835         
26836         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
26837         // selectNode - might want to handle IE?
26838         if (ev &&
26839             (ev.type == 'mouseup' || ev.type == 'click' ) &&
26840             ev.target && ev.target.tagName == 'IMG') {
26841             // they have click on an image...
26842             // let's see if we can change the selection...
26843             sel = ev.target;
26844          
26845               var nodeRange = sel.ownerDocument.createRange();
26846             try {
26847                 nodeRange.selectNode(sel);
26848             } catch (e) {
26849                 nodeRange.selectNodeContents(sel);
26850             }
26851             //nodeRange.collapse(true);
26852             var s = editor.win.getSelection();
26853             s.removeAllRanges();
26854             s.addRange(nodeRange);
26855         }  
26856         
26857       
26858         var updateFooter = sel ? false : true;
26859         
26860         
26861         var ans = this.editor.getAllAncestors();
26862         
26863         // pick
26864         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26865         
26866         if (!sel) { 
26867             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
26868             sel = sel ? sel : this.editor.doc.body;
26869             sel = sel.tagName.length ? sel : this.editor.doc.body;
26870             
26871         }
26872         // pick a menu that exists..
26873         var tn = sel.tagName.toUpperCase();
26874         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
26875         
26876         tn = sel.tagName.toUpperCase();
26877         
26878         var lastSel = this.tb.selectedNode
26879         
26880         this.tb.selectedNode = sel;
26881         
26882         // if current menu does not match..
26883         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
26884                 
26885             this.tb.el.hide();
26886             ///console.log("show: " + tn);
26887             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
26888             this.tb.el.show();
26889             // update name
26890             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
26891             
26892             
26893             // update attributes
26894             if (this.tb.fields) {
26895                 this.tb.fields.each(function(e) {
26896                    e.setValue(sel.getAttribute(e.attrname));
26897                 });
26898             }
26899             
26900             var hasStyles = false;
26901             for(var i in this.styles) {
26902                 hasStyles = true;
26903                 break;
26904             }
26905             
26906             // update styles
26907             if (hasStyles) { 
26908                 var st = this.tb.fields.item(0);
26909                 
26910                 st.store.removeAll();
26911                
26912                 
26913                 var cn = sel.className.split(/\s+/);
26914                 
26915                 var avs = [];
26916                 if (this.styles['*']) {
26917                     
26918                     Roo.each(this.styles['*'], function(v) {
26919                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
26920                     });
26921                 }
26922                 if (this.styles[tn]) { 
26923                     Roo.each(this.styles[tn], function(v) {
26924                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
26925                     });
26926                 }
26927                 
26928                 st.store.loadData(avs);
26929                 st.collapse();
26930                 st.setValue(cn);
26931             }
26932             // flag our selected Node.
26933             this.tb.selectedNode = sel;
26934            
26935            
26936             Roo.menu.MenuMgr.hideAll();
26937
26938         }
26939         
26940         if (!updateFooter) {
26941             return;
26942         }
26943         // update the footer
26944         //
26945         var html = '';
26946         
26947         this.footerEls = ans.reverse();
26948         Roo.each(this.footerEls, function(a,i) {
26949             if (!a) { return; }
26950             html += html.length ? ' &gt; '  :  '';
26951             
26952             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
26953             
26954         });
26955        
26956         // 
26957         var sz = this.footDisp.up('td').getSize();
26958         this.footDisp.dom.style.width = (sz.width -10) + 'px';
26959         this.footDisp.dom.style.marginLeft = '5px';
26960         
26961         this.footDisp.dom.style.overflow = 'hidden';
26962         
26963         this.footDisp.dom.innerHTML = html;
26964             
26965         //this.editorsyncValue();
26966     },
26967    
26968        
26969     // private
26970     onDestroy : function(){
26971         if(this.rendered){
26972             
26973             this.tb.items.each(function(item){
26974                 if(item.menu){
26975                     item.menu.removeAll();
26976                     if(item.menu.el){
26977                         item.menu.el.destroy();
26978                     }
26979                 }
26980                 item.destroy();
26981             });
26982              
26983         }
26984     },
26985     onFirstFocus: function() {
26986         // need to do this for all the toolbars..
26987         this.tb.items.each(function(item){
26988            item.enable();
26989         });
26990     },
26991     buildToolbar: function(tlist, nm)
26992     {
26993         var editor = this.editor;
26994          // create a new element.
26995         var wdiv = editor.wrap.createChild({
26996                 tag: 'div'
26997             }, editor.wrap.dom.firstChild.nextSibling, true);
26998         
26999        
27000         var tb = new Roo.Toolbar(wdiv);
27001         // add the name..
27002         
27003         tb.add(nm+ ":&nbsp;");
27004         
27005         var styles = [];
27006         for(var i in this.styles) {
27007             styles.push(i);
27008         }
27009         
27010         // styles...
27011         if (styles && styles.length) {
27012             
27013             // this needs a multi-select checkbox...
27014             tb.addField( new Roo.form.ComboBox({
27015                 store: new Roo.data.SimpleStore({
27016                     id : 'val',
27017                     fields: ['val', 'selected'],
27018                     data : [] 
27019                 }),
27020                 name : '-roo-edit-className',
27021                 attrname : 'className',
27022                 displayField:'val',
27023                 typeAhead: false,
27024                 mode: 'local',
27025                 editable : false,
27026                 triggerAction: 'all',
27027                 emptyText:'Select Style',
27028                 selectOnFocus:true,
27029                 width: 130,
27030                 listeners : {
27031                     'select': function(c, r, i) {
27032                         // initial support only for on class per el..
27033                         tb.selectedNode.className =  r ? r.get('val') : '';
27034                         editor.syncValue();
27035                     }
27036                 }
27037     
27038             }));
27039         }
27040             
27041         
27042         
27043         for (var i in tlist) {
27044             
27045             var item = tlist[i];
27046             tb.add(item.title + ":&nbsp;");
27047             
27048             
27049             
27050             
27051             if (item.opts) {
27052                 // opts == pulldown..
27053                 tb.addField( new Roo.form.ComboBox({
27054                     store: new Roo.data.SimpleStore({
27055                         id : 'val',
27056                         fields: ['val'],
27057                         data : item.opts  
27058                     }),
27059                     name : '-roo-edit-' + i,
27060                     attrname : i,
27061                     displayField:'val',
27062                     typeAhead: false,
27063                     mode: 'local',
27064                     editable : false,
27065                     triggerAction: 'all',
27066                     emptyText:'Select',
27067                     selectOnFocus:true,
27068                     width: item.width ? item.width  : 130,
27069                     listeners : {
27070                         'select': function(c, r, i) {
27071                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27072                         }
27073                     }
27074
27075                 }));
27076                 continue;
27077                     
27078                  
27079                 
27080                 tb.addField( new Roo.form.TextField({
27081                     name: i,
27082                     width: 100,
27083                     //allowBlank:false,
27084                     value: ''
27085                 }));
27086                 continue;
27087             }
27088             tb.addField( new Roo.form.TextField({
27089                 name: '-roo-edit-' + i,
27090                 attrname : i,
27091                 
27092                 width: item.width,
27093                 //allowBlank:true,
27094                 value: '',
27095                 listeners: {
27096                     'change' : function(f, nv, ov) {
27097                         tb.selectedNode.setAttribute(f.attrname, nv);
27098                     }
27099                 }
27100             }));
27101              
27102         }
27103         tb.el.on('click', function(e){
27104             e.preventDefault(); // what does this do?
27105         });
27106         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27107         tb.el.hide();
27108         tb.name = nm;
27109         // dont need to disable them... as they will get hidden
27110         return tb;
27111          
27112         
27113     },
27114     buildFooter : function()
27115     {
27116         
27117         var fel = this.editor.wrap.createChild();
27118         this.footer = new Roo.Toolbar(fel);
27119         // toolbar has scrolly on left / right?
27120         var footDisp= new Roo.Toolbar.Fill();
27121         var _t = this;
27122         this.footer.add(
27123             {
27124                 text : '&lt;',
27125                 xtype: 'Button',
27126                 handler : function() {
27127                     _t.footDisp.scrollTo('left',0,true)
27128                 }
27129             }
27130         );
27131         this.footer.add( footDisp );
27132         this.footer.add( 
27133             {
27134                 text : '&gt;',
27135                 xtype: 'Button',
27136                 handler : function() {
27137                     // no animation..
27138                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27139                 }
27140             }
27141         );
27142         var fel = Roo.get(footDisp.el);
27143         fel.addClass('x-editor-context');
27144         this.footDispWrap = fel; 
27145         this.footDispWrap.overflow  = 'hidden';
27146         
27147         this.footDisp = fel.createChild();
27148         this.footDispWrap.on('click', this.onContextClick, this)
27149         
27150         
27151     },
27152     onContextClick : function (ev,dom)
27153     {
27154         ev.preventDefault();
27155         var  cn = dom.className;
27156         Roo.log(cn);
27157         if (!cn.match(/x-ed-loc-/)) {
27158             return;
27159         }
27160         var n = cn.split('-').pop();
27161         var ans = this.footerEls;
27162         var sel = ans[n];
27163         
27164          // pick
27165         var range = this.editor.createRange();
27166         
27167         range.selectNodeContents(sel);
27168         //range.selectNode(sel);
27169         
27170         
27171         var selection = this.editor.getSelection();
27172         selection.removeAllRanges();
27173         selection.addRange(range);
27174         
27175         
27176         
27177         this.updateToolbar(null, null, sel);
27178         
27179         
27180     }
27181     
27182     
27183     
27184     
27185     
27186 });
27187
27188
27189
27190
27191
27192 /*
27193  * Based on:
27194  * Ext JS Library 1.1.1
27195  * Copyright(c) 2006-2007, Ext JS, LLC.
27196  *
27197  * Originally Released Under LGPL - original licence link has changed is not relivant.
27198  *
27199  * Fork - LGPL
27200  * <script type="text/javascript">
27201  */
27202  
27203 /**
27204  * @class Roo.form.BasicForm
27205  * @extends Roo.util.Observable
27206  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
27207  * @constructor
27208  * @param {String/HTMLElement/Roo.Element} el The form element or its id
27209  * @param {Object} config Configuration options
27210  */
27211 Roo.form.BasicForm = function(el, config){
27212     this.allItems = [];
27213     this.childForms = [];
27214     Roo.apply(this, config);
27215     /*
27216      * The Roo.form.Field items in this form.
27217      * @type MixedCollection
27218      */
27219      
27220      
27221     this.items = new Roo.util.MixedCollection(false, function(o){
27222         return o.id || (o.id = Roo.id());
27223     });
27224     this.addEvents({
27225         /**
27226          * @event beforeaction
27227          * Fires before any action is performed. Return false to cancel the action.
27228          * @param {Form} this
27229          * @param {Action} action The action to be performed
27230          */
27231         beforeaction: true,
27232         /**
27233          * @event actionfailed
27234          * Fires when an action fails.
27235          * @param {Form} this
27236          * @param {Action} action The action that failed
27237          */
27238         actionfailed : true,
27239         /**
27240          * @event actioncomplete
27241          * Fires when an action is completed.
27242          * @param {Form} this
27243          * @param {Action} action The action that completed
27244          */
27245         actioncomplete : true
27246     });
27247     if(el){
27248         this.initEl(el);
27249     }
27250     Roo.form.BasicForm.superclass.constructor.call(this);
27251 };
27252
27253 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
27254     /**
27255      * @cfg {String} method
27256      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
27257      */
27258     /**
27259      * @cfg {DataReader} reader
27260      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
27261      * This is optional as there is built-in support for processing JSON.
27262      */
27263     /**
27264      * @cfg {DataReader} errorReader
27265      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
27266      * This is completely optional as there is built-in support for processing JSON.
27267      */
27268     /**
27269      * @cfg {String} url
27270      * The URL to use for form actions if one isn't supplied in the action options.
27271      */
27272     /**
27273      * @cfg {Boolean} fileUpload
27274      * Set to true if this form is a file upload.
27275      */
27276      
27277     /**
27278      * @cfg {Object} baseParams
27279      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
27280      */
27281      /**
27282      
27283     /**
27284      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
27285      */
27286     timeout: 30,
27287
27288     // private
27289     activeAction : null,
27290
27291     /**
27292      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
27293      * or setValues() data instead of when the form was first created.
27294      */
27295     trackResetOnLoad : false,
27296     
27297     
27298     /**
27299      * childForms - used for multi-tab forms
27300      * @type {Array}
27301      */
27302     childForms : false,
27303     
27304     /**
27305      * allItems - full list of fields.
27306      * @type {Array}
27307      */
27308     allItems : false,
27309     
27310     /**
27311      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
27312      * element by passing it or its id or mask the form itself by passing in true.
27313      * @type Mixed
27314      */
27315     waitMsgTarget : false,
27316
27317     // private
27318     initEl : function(el){
27319         this.el = Roo.get(el);
27320         this.id = this.el.id || Roo.id();
27321         this.el.on('submit', this.onSubmit, this);
27322         this.el.addClass('x-form');
27323     },
27324
27325     // private
27326     onSubmit : function(e){
27327         e.stopEvent();
27328     },
27329
27330     /**
27331      * Returns true if client-side validation on the form is successful.
27332      * @return Boolean
27333      */
27334     isValid : function(){
27335         var valid = true;
27336         this.items.each(function(f){
27337            if(!f.validate()){
27338                valid = false;
27339            }
27340         });
27341         return valid;
27342     },
27343
27344     /**
27345      * Returns true if any fields in this form have changed since their original load.
27346      * @return Boolean
27347      */
27348     isDirty : function(){
27349         var dirty = false;
27350         this.items.each(function(f){
27351            if(f.isDirty()){
27352                dirty = true;
27353                return false;
27354            }
27355         });
27356         return dirty;
27357     },
27358
27359     /**
27360      * Performs a predefined action (submit or load) or custom actions you define on this form.
27361      * @param {String} actionName The name of the action type
27362      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
27363      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
27364      * accept other config options):
27365      * <pre>
27366 Property          Type             Description
27367 ----------------  ---------------  ----------------------------------------------------------------------------------
27368 url               String           The url for the action (defaults to the form's url)
27369 method            String           The form method to use (defaults to the form's method, or POST if not defined)
27370 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
27371 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
27372                                    validate the form on the client (defaults to false)
27373      * </pre>
27374      * @return {BasicForm} this
27375      */
27376     doAction : function(action, options){
27377         if(typeof action == 'string'){
27378             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
27379         }
27380         if(this.fireEvent('beforeaction', this, action) !== false){
27381             this.beforeAction(action);
27382             action.run.defer(100, action);
27383         }
27384         return this;
27385     },
27386
27387     /**
27388      * Shortcut to do a submit action.
27389      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27390      * @return {BasicForm} this
27391      */
27392     submit : function(options){
27393         this.doAction('submit', options);
27394         return this;
27395     },
27396
27397     /**
27398      * Shortcut to do a load action.
27399      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27400      * @return {BasicForm} this
27401      */
27402     load : function(options){
27403         this.doAction('load', options);
27404         return this;
27405     },
27406
27407     /**
27408      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
27409      * @param {Record} record The record to edit
27410      * @return {BasicForm} this
27411      */
27412     updateRecord : function(record){
27413         record.beginEdit();
27414         var fs = record.fields;
27415         fs.each(function(f){
27416             var field = this.findField(f.name);
27417             if(field){
27418                 record.set(f.name, field.getValue());
27419             }
27420         }, this);
27421         record.endEdit();
27422         return this;
27423     },
27424
27425     /**
27426      * Loads an Roo.data.Record into this form.
27427      * @param {Record} record The record to load
27428      * @return {BasicForm} this
27429      */
27430     loadRecord : function(record){
27431         this.setValues(record.data);
27432         return this;
27433     },
27434
27435     // private
27436     beforeAction : function(action){
27437         var o = action.options;
27438         
27439        
27440         if(this.waitMsgTarget === true){
27441             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
27442         }else if(this.waitMsgTarget){
27443             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
27444             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
27445         }else {
27446             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
27447         }
27448          
27449     },
27450
27451     // private
27452     afterAction : function(action, success){
27453         this.activeAction = null;
27454         var o = action.options;
27455         
27456         if(this.waitMsgTarget === true){
27457             this.el.unmask();
27458         }else if(this.waitMsgTarget){
27459             this.waitMsgTarget.unmask();
27460         }else{
27461             Roo.MessageBox.updateProgress(1);
27462             Roo.MessageBox.hide();
27463         }
27464          
27465         if(success){
27466             if(o.reset){
27467                 this.reset();
27468             }
27469             Roo.callback(o.success, o.scope, [this, action]);
27470             this.fireEvent('actioncomplete', this, action);
27471             
27472         }else{
27473             
27474             // failure condition..
27475             // we have a scenario where updates need confirming.
27476             // eg. if a locking scenario exists..
27477             // we look for { errors : { needs_confirm : true }} in the response.
27478             if (
27479                 (typeof(action.result) != 'undefined')  &&
27480                 (typeof(action.result.errors) != 'undefined')  &&
27481                 (typeof(action.result.errors.needs_confirm) != 'undefined')
27482           ){
27483                 var _t = this;
27484                 Roo.MessageBox.confirm(
27485                     "Change requires confirmation",
27486                     action.result.errorMsg,
27487                     function(r) {
27488                         if (r != 'yes') {
27489                             return;
27490                         }
27491                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
27492                     }
27493                     
27494                 );
27495                 
27496                 
27497                 
27498                 return;
27499             }
27500             
27501             Roo.callback(o.failure, o.scope, [this, action]);
27502             // show an error message if no failed handler is set..
27503             if (!this.hasListener('actionfailed')) {
27504                 Roo.MessageBox.alert("Error",
27505                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
27506                         action.result.errorMsg :
27507                         "Saving Failed, please check your entries or try again"
27508                 );
27509             }
27510             
27511             this.fireEvent('actionfailed', this, action);
27512         }
27513         
27514     },
27515
27516     /**
27517      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
27518      * @param {String} id The value to search for
27519      * @return Field
27520      */
27521     findField : function(id){
27522         var field = this.items.get(id);
27523         if(!field){
27524             this.items.each(function(f){
27525                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
27526                     field = f;
27527                     return false;
27528                 }
27529             });
27530         }
27531         return field || null;
27532     },
27533
27534     /**
27535      * Add a secondary form to this one, 
27536      * Used to provide tabbed forms. One form is primary, with hidden values 
27537      * which mirror the elements from the other forms.
27538      * 
27539      * @param {Roo.form.Form} form to add.
27540      * 
27541      */
27542     addForm : function(form)
27543     {
27544        
27545         if (this.childForms.indexOf(form) > -1) {
27546             // already added..
27547             return;
27548         }
27549         this.childForms.push(form);
27550         var n = '';
27551         Roo.each(form.allItems, function (fe) {
27552             
27553             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
27554             if (this.findField(n)) { // already added..
27555                 return;
27556             }
27557             var add = new Roo.form.Hidden({
27558                 name : n
27559             });
27560             add.render(this.el);
27561             
27562             this.add( add );
27563         }, this);
27564         
27565     },
27566     /**
27567      * Mark fields in this form invalid in bulk.
27568      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
27569      * @return {BasicForm} this
27570      */
27571     markInvalid : function(errors){
27572         if(errors instanceof Array){
27573             for(var i = 0, len = errors.length; i < len; i++){
27574                 var fieldError = errors[i];
27575                 var f = this.findField(fieldError.id);
27576                 if(f){
27577                     f.markInvalid(fieldError.msg);
27578                 }
27579             }
27580         }else{
27581             var field, id;
27582             for(id in errors){
27583                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
27584                     field.markInvalid(errors[id]);
27585                 }
27586             }
27587         }
27588         Roo.each(this.childForms || [], function (f) {
27589             f.markInvalid(errors);
27590         });
27591         
27592         return this;
27593     },
27594
27595     /**
27596      * Set values for fields in this form in bulk.
27597      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
27598      * @return {BasicForm} this
27599      */
27600     setValues : function(values){
27601         if(values instanceof Array){ // array of objects
27602             for(var i = 0, len = values.length; i < len; i++){
27603                 var v = values[i];
27604                 var f = this.findField(v.id);
27605                 if(f){
27606                     f.setValue(v.value);
27607                     if(this.trackResetOnLoad){
27608                         f.originalValue = f.getValue();
27609                     }
27610                 }
27611             }
27612         }else{ // object hash
27613             var field, id;
27614             for(id in values){
27615                 if(typeof values[id] != 'function' && (field = this.findField(id))){
27616                     
27617                     if (field.setFromData && 
27618                         field.valueField && 
27619                         field.displayField &&
27620                         // combos' with local stores can 
27621                         // be queried via setValue()
27622                         // to set their value..
27623                         (field.store && !field.store.isLocal)
27624                         ) {
27625                         // it's a combo
27626                         var sd = { };
27627                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
27628                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
27629                         field.setFromData(sd);
27630                         
27631                     } else {
27632                         field.setValue(values[id]);
27633                     }
27634                     
27635                     
27636                     if(this.trackResetOnLoad){
27637                         field.originalValue = field.getValue();
27638                     }
27639                 }
27640             }
27641         }
27642          
27643         Roo.each(this.childForms || [], function (f) {
27644             f.setValues(values);
27645         });
27646                 
27647         return this;
27648     },
27649
27650     /**
27651      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
27652      * they are returned as an array.
27653      * @param {Boolean} asString
27654      * @return {Object}
27655      */
27656     getValues : function(asString){
27657         if (this.childForms) {
27658             // copy values from the child forms
27659             Roo.each(this.childForms, function (f) {
27660                 this.setValues(f.getValues());
27661             }, this);
27662         }
27663         
27664         
27665         
27666         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
27667         if(asString === true){
27668             return fs;
27669         }
27670         return Roo.urlDecode(fs);
27671     },
27672     
27673     /**
27674      * Returns the fields in this form as an object with key/value pairs. 
27675      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
27676      * @return {Object}
27677      */
27678     getFieldValues : function(with_hidden)
27679     {
27680         if (this.childForms) {
27681             // copy values from the child forms
27682             // should this call getFieldValues - probably not as we do not currently copy
27683             // hidden fields when we generate..
27684             Roo.each(this.childForms, function (f) {
27685                 this.setValues(f.getValues());
27686             }, this);
27687         }
27688         
27689         var ret = {};
27690         this.items.each(function(f){
27691             if (!f.getName()) {
27692                 return;
27693             }
27694             var v = f.getValue();
27695             // not sure if this supported any more..
27696             if ((typeof(v) == 'object') && f.getRawValue) {
27697                 v = f.getRawValue() ; // dates..
27698             }
27699             // combo boxes where name != hiddenName...
27700             if (f.name != f.getName()) {
27701                 ret[f.name] = f.getRawValue();
27702             }
27703             ret[f.getName()] = v;
27704         });
27705         
27706         return ret;
27707     },
27708
27709     /**
27710      * Clears all invalid messages in this form.
27711      * @return {BasicForm} this
27712      */
27713     clearInvalid : function(){
27714         this.items.each(function(f){
27715            f.clearInvalid();
27716         });
27717         
27718         Roo.each(this.childForms || [], function (f) {
27719             f.clearInvalid();
27720         });
27721         
27722         
27723         return this;
27724     },
27725
27726     /**
27727      * Resets this form.
27728      * @return {BasicForm} this
27729      */
27730     reset : function(){
27731         this.items.each(function(f){
27732             f.reset();
27733         });
27734         
27735         Roo.each(this.childForms || [], function (f) {
27736             f.reset();
27737         });
27738        
27739         
27740         return this;
27741     },
27742
27743     /**
27744      * Add Roo.form components to this form.
27745      * @param {Field} field1
27746      * @param {Field} field2 (optional)
27747      * @param {Field} etc (optional)
27748      * @return {BasicForm} this
27749      */
27750     add : function(){
27751         this.items.addAll(Array.prototype.slice.call(arguments, 0));
27752         return this;
27753     },
27754
27755
27756     /**
27757      * Removes a field from the items collection (does NOT remove its markup).
27758      * @param {Field} field
27759      * @return {BasicForm} this
27760      */
27761     remove : function(field){
27762         this.items.remove(field);
27763         return this;
27764     },
27765
27766     /**
27767      * Looks at the fields in this form, checks them for an id attribute,
27768      * and calls applyTo on the existing dom element with that id.
27769      * @return {BasicForm} this
27770      */
27771     render : function(){
27772         this.items.each(function(f){
27773             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
27774                 f.applyTo(f.id);
27775             }
27776         });
27777         return this;
27778     },
27779
27780     /**
27781      * Calls {@link Ext#apply} for all fields in this form with the passed object.
27782      * @param {Object} values
27783      * @return {BasicForm} this
27784      */
27785     applyToFields : function(o){
27786         this.items.each(function(f){
27787            Roo.apply(f, o);
27788         });
27789         return this;
27790     },
27791
27792     /**
27793      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
27794      * @param {Object} values
27795      * @return {BasicForm} this
27796      */
27797     applyIfToFields : function(o){
27798         this.items.each(function(f){
27799            Roo.applyIf(f, o);
27800         });
27801         return this;
27802     }
27803 });
27804
27805 // back compat
27806 Roo.BasicForm = Roo.form.BasicForm;/*
27807  * Based on:
27808  * Ext JS Library 1.1.1
27809  * Copyright(c) 2006-2007, Ext JS, LLC.
27810  *
27811  * Originally Released Under LGPL - original licence link has changed is not relivant.
27812  *
27813  * Fork - LGPL
27814  * <script type="text/javascript">
27815  */
27816
27817 /**
27818  * @class Roo.form.Form
27819  * @extends Roo.form.BasicForm
27820  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
27821  * @constructor
27822  * @param {Object} config Configuration options
27823  */
27824 Roo.form.Form = function(config){
27825     var xitems =  [];
27826     if (config.items) {
27827         xitems = config.items;
27828         delete config.items;
27829     }
27830    
27831     
27832     Roo.form.Form.superclass.constructor.call(this, null, config);
27833     this.url = this.url || this.action;
27834     if(!this.root){
27835         this.root = new Roo.form.Layout(Roo.applyIf({
27836             id: Roo.id()
27837         }, config));
27838     }
27839     this.active = this.root;
27840     /**
27841      * Array of all the buttons that have been added to this form via {@link addButton}
27842      * @type Array
27843      */
27844     this.buttons = [];
27845     this.allItems = [];
27846     this.addEvents({
27847         /**
27848          * @event clientvalidation
27849          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
27850          * @param {Form} this
27851          * @param {Boolean} valid true if the form has passed client-side validation
27852          */
27853         clientvalidation: true,
27854         /**
27855          * @event rendered
27856          * Fires when the form is rendered
27857          * @param {Roo.form.Form} form
27858          */
27859         rendered : true
27860     });
27861     
27862     if (this.progressUrl) {
27863             // push a hidden field onto the list of fields..
27864             this.addxtype( {
27865                     xns: Roo.form, 
27866                     xtype : 'Hidden', 
27867                     name : 'UPLOAD_IDENTIFIER' 
27868             });
27869         }
27870         
27871     
27872     Roo.each(xitems, this.addxtype, this);
27873     
27874     
27875     
27876 };
27877
27878 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
27879     /**
27880      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
27881      */
27882     /**
27883      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
27884      */
27885     /**
27886      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
27887      */
27888     buttonAlign:'center',
27889
27890     /**
27891      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
27892      */
27893     minButtonWidth:75,
27894
27895     /**
27896      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
27897      * This property cascades to child containers if not set.
27898      */
27899     labelAlign:'left',
27900
27901     /**
27902      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
27903      * fires a looping event with that state. This is required to bind buttons to the valid
27904      * state using the config value formBind:true on the button.
27905      */
27906     monitorValid : false,
27907
27908     /**
27909      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
27910      */
27911     monitorPoll : 200,
27912     
27913     /**
27914      * @cfg {String} progressUrl - Url to return progress data 
27915      */
27916     
27917     progressUrl : false,
27918   
27919     /**
27920      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
27921      * fields are added and the column is closed. If no fields are passed the column remains open
27922      * until end() is called.
27923      * @param {Object} config The config to pass to the column
27924      * @param {Field} field1 (optional)
27925      * @param {Field} field2 (optional)
27926      * @param {Field} etc (optional)
27927      * @return Column The column container object
27928      */
27929     column : function(c){
27930         var col = new Roo.form.Column(c);
27931         this.start(col);
27932         if(arguments.length > 1){ // duplicate code required because of Opera
27933             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27934             this.end();
27935         }
27936         return col;
27937     },
27938
27939     /**
27940      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
27941      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
27942      * until end() is called.
27943      * @param {Object} config The config to pass to the fieldset
27944      * @param {Field} field1 (optional)
27945      * @param {Field} field2 (optional)
27946      * @param {Field} etc (optional)
27947      * @return FieldSet The fieldset container object
27948      */
27949     fieldset : function(c){
27950         var fs = new Roo.form.FieldSet(c);
27951         this.start(fs);
27952         if(arguments.length > 1){ // duplicate code required because of Opera
27953             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27954             this.end();
27955         }
27956         return fs;
27957     },
27958
27959     /**
27960      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
27961      * fields are added and the container is closed. If no fields are passed the container remains open
27962      * until end() is called.
27963      * @param {Object} config The config to pass to the Layout
27964      * @param {Field} field1 (optional)
27965      * @param {Field} field2 (optional)
27966      * @param {Field} etc (optional)
27967      * @return Layout The container object
27968      */
27969     container : function(c){
27970         var l = new Roo.form.Layout(c);
27971         this.start(l);
27972         if(arguments.length > 1){ // duplicate code required because of Opera
27973             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27974             this.end();
27975         }
27976         return l;
27977     },
27978
27979     /**
27980      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
27981      * @param {Object} container A Roo.form.Layout or subclass of Layout
27982      * @return {Form} this
27983      */
27984     start : function(c){
27985         // cascade label info
27986         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
27987         this.active.stack.push(c);
27988         c.ownerCt = this.active;
27989         this.active = c;
27990         return this;
27991     },
27992
27993     /**
27994      * Closes the current open container
27995      * @return {Form} this
27996      */
27997     end : function(){
27998         if(this.active == this.root){
27999             return this;
28000         }
28001         this.active = this.active.ownerCt;
28002         return this;
28003     },
28004
28005     /**
28006      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28007      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28008      * as the label of the field.
28009      * @param {Field} field1
28010      * @param {Field} field2 (optional)
28011      * @param {Field} etc. (optional)
28012      * @return {Form} this
28013      */
28014     add : function(){
28015         this.active.stack.push.apply(this.active.stack, arguments);
28016         this.allItems.push.apply(this.allItems,arguments);
28017         var r = [];
28018         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28019             if(a[i].isFormField){
28020                 r.push(a[i]);
28021             }
28022         }
28023         if(r.length > 0){
28024             Roo.form.Form.superclass.add.apply(this, r);
28025         }
28026         return this;
28027     },
28028     
28029
28030     
28031     
28032     
28033      /**
28034      * Find any element that has been added to a form, using it's ID or name
28035      * This can include framesets, columns etc. along with regular fields..
28036      * @param {String} id - id or name to find.
28037      
28038      * @return {Element} e - or false if nothing found.
28039      */
28040     findbyId : function(id)
28041     {
28042         var ret = false;
28043         if (!id) {
28044             return ret;
28045         }
28046         Roo.each(this.allItems, function(f){
28047             if (f.id == id || f.name == id ){
28048                 ret = f;
28049                 return false;
28050             }
28051         });
28052         return ret;
28053     },
28054
28055     
28056     
28057     /**
28058      * Render this form into the passed container. This should only be called once!
28059      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28060      * @return {Form} this
28061      */
28062     render : function(ct)
28063     {
28064         
28065         
28066         
28067         ct = Roo.get(ct);
28068         var o = this.autoCreate || {
28069             tag: 'form',
28070             method : this.method || 'POST',
28071             id : this.id || Roo.id()
28072         };
28073         this.initEl(ct.createChild(o));
28074
28075         this.root.render(this.el);
28076         
28077        
28078              
28079         this.items.each(function(f){
28080             f.render('x-form-el-'+f.id);
28081         });
28082
28083         if(this.buttons.length > 0){
28084             // tables are required to maintain order and for correct IE layout
28085             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28086                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28087                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28088             }}, null, true);
28089             var tr = tb.getElementsByTagName('tr')[0];
28090             for(var i = 0, len = this.buttons.length; i < len; i++) {
28091                 var b = this.buttons[i];
28092                 var td = document.createElement('td');
28093                 td.className = 'x-form-btn-td';
28094                 b.render(tr.appendChild(td));
28095             }
28096         }
28097         if(this.monitorValid){ // initialize after render
28098             this.startMonitoring();
28099         }
28100         this.fireEvent('rendered', this);
28101         return this;
28102     },
28103
28104     /**
28105      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28106      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28107      * object or a valid Roo.DomHelper element config
28108      * @param {Function} handler The function called when the button is clicked
28109      * @param {Object} scope (optional) The scope of the handler function
28110      * @return {Roo.Button}
28111      */
28112     addButton : function(config, handler, scope){
28113         var bc = {
28114             handler: handler,
28115             scope: scope,
28116             minWidth: this.minButtonWidth,
28117             hideParent:true
28118         };
28119         if(typeof config == "string"){
28120             bc.text = config;
28121         }else{
28122             Roo.apply(bc, config);
28123         }
28124         var btn = new Roo.Button(null, bc);
28125         this.buttons.push(btn);
28126         return btn;
28127     },
28128
28129      /**
28130      * Adds a series of form elements (using the xtype property as the factory method.
28131      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28132      * @param {Object} config 
28133      */
28134     
28135     addxtype : function()
28136     {
28137         var ar = Array.prototype.slice.call(arguments, 0);
28138         var ret = false;
28139         for(var i = 0; i < ar.length; i++) {
28140             if (!ar[i]) {
28141                 continue; // skip -- if this happends something invalid got sent, we 
28142                 // should ignore it, as basically that interface element will not show up
28143                 // and that should be pretty obvious!!
28144             }
28145             
28146             if (Roo.form[ar[i].xtype]) {
28147                 ar[i].form = this;
28148                 var fe = Roo.factory(ar[i], Roo.form);
28149                 if (!ret) {
28150                     ret = fe;
28151                 }
28152                 fe.form = this;
28153                 if (fe.store) {
28154                     fe.store.form = this;
28155                 }
28156                 if (fe.isLayout) {  
28157                          
28158                     this.start(fe);
28159                     this.allItems.push(fe);
28160                     if (fe.items && fe.addxtype) {
28161                         fe.addxtype.apply(fe, fe.items);
28162                         delete fe.items;
28163                     }
28164                      this.end();
28165                     continue;
28166                 }
28167                 
28168                 
28169                  
28170                 this.add(fe);
28171               //  console.log('adding ' + ar[i].xtype);
28172             }
28173             if (ar[i].xtype == 'Button') {  
28174                 //console.log('adding button');
28175                 //console.log(ar[i]);
28176                 this.addButton(ar[i]);
28177                 this.allItems.push(fe);
28178                 continue;
28179             }
28180             
28181             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
28182                 alert('end is not supported on xtype any more, use items');
28183             //    this.end();
28184             //    //console.log('adding end');
28185             }
28186             
28187         }
28188         return ret;
28189     },
28190     
28191     /**
28192      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
28193      * option "monitorValid"
28194      */
28195     startMonitoring : function(){
28196         if(!this.bound){
28197             this.bound = true;
28198             Roo.TaskMgr.start({
28199                 run : this.bindHandler,
28200                 interval : this.monitorPoll || 200,
28201                 scope: this
28202             });
28203         }
28204     },
28205
28206     /**
28207      * Stops monitoring of the valid state of this form
28208      */
28209     stopMonitoring : function(){
28210         this.bound = false;
28211     },
28212
28213     // private
28214     bindHandler : function(){
28215         if(!this.bound){
28216             return false; // stops binding
28217         }
28218         var valid = true;
28219         this.items.each(function(f){
28220             if(!f.isValid(true)){
28221                 valid = false;
28222                 return false;
28223             }
28224         });
28225         for(var i = 0, len = this.buttons.length; i < len; i++){
28226             var btn = this.buttons[i];
28227             if(btn.formBind === true && btn.disabled === valid){
28228                 btn.setDisabled(!valid);
28229             }
28230         }
28231         this.fireEvent('clientvalidation', this, valid);
28232     }
28233     
28234     
28235     
28236     
28237     
28238     
28239     
28240     
28241 });
28242
28243
28244 // back compat
28245 Roo.Form = Roo.form.Form;
28246 /*
28247  * Based on:
28248  * Ext JS Library 1.1.1
28249  * Copyright(c) 2006-2007, Ext JS, LLC.
28250  *
28251  * Originally Released Under LGPL - original licence link has changed is not relivant.
28252  *
28253  * Fork - LGPL
28254  * <script type="text/javascript">
28255  */
28256  
28257  /**
28258  * @class Roo.form.Action
28259  * Internal Class used to handle form actions
28260  * @constructor
28261  * @param {Roo.form.BasicForm} el The form element or its id
28262  * @param {Object} config Configuration options
28263  */
28264  
28265  
28266 // define the action interface
28267 Roo.form.Action = function(form, options){
28268     this.form = form;
28269     this.options = options || {};
28270 };
28271 /**
28272  * Client Validation Failed
28273  * @const 
28274  */
28275 Roo.form.Action.CLIENT_INVALID = 'client';
28276 /**
28277  * Server Validation Failed
28278  * @const 
28279  */
28280  Roo.form.Action.SERVER_INVALID = 'server';
28281  /**
28282  * Connect to Server Failed
28283  * @const 
28284  */
28285 Roo.form.Action.CONNECT_FAILURE = 'connect';
28286 /**
28287  * Reading Data from Server Failed
28288  * @const 
28289  */
28290 Roo.form.Action.LOAD_FAILURE = 'load';
28291
28292 Roo.form.Action.prototype = {
28293     type : 'default',
28294     failureType : undefined,
28295     response : undefined,
28296     result : undefined,
28297
28298     // interface method
28299     run : function(options){
28300
28301     },
28302
28303     // interface method
28304     success : function(response){
28305
28306     },
28307
28308     // interface method
28309     handleResponse : function(response){
28310
28311     },
28312
28313     // default connection failure
28314     failure : function(response){
28315         
28316         this.response = response;
28317         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28318         this.form.afterAction(this, false);
28319     },
28320
28321     processResponse : function(response){
28322         this.response = response;
28323         if(!response.responseText){
28324             return true;
28325         }
28326         this.result = this.handleResponse(response);
28327         return this.result;
28328     },
28329
28330     // utility functions used internally
28331     getUrl : function(appendParams){
28332         var url = this.options.url || this.form.url || this.form.el.dom.action;
28333         if(appendParams){
28334             var p = this.getParams();
28335             if(p){
28336                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
28337             }
28338         }
28339         return url;
28340     },
28341
28342     getMethod : function(){
28343         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
28344     },
28345
28346     getParams : function(){
28347         var bp = this.form.baseParams;
28348         var p = this.options.params;
28349         if(p){
28350             if(typeof p == "object"){
28351                 p = Roo.urlEncode(Roo.applyIf(p, bp));
28352             }else if(typeof p == 'string' && bp){
28353                 p += '&' + Roo.urlEncode(bp);
28354             }
28355         }else if(bp){
28356             p = Roo.urlEncode(bp);
28357         }
28358         return p;
28359     },
28360
28361     createCallback : function(){
28362         return {
28363             success: this.success,
28364             failure: this.failure,
28365             scope: this,
28366             timeout: (this.form.timeout*1000),
28367             upload: this.form.fileUpload ? this.success : undefined
28368         };
28369     }
28370 };
28371
28372 Roo.form.Action.Submit = function(form, options){
28373     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
28374 };
28375
28376 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
28377     type : 'submit',
28378
28379     haveProgress : false,
28380     uploadComplete : false,
28381     
28382     // uploadProgress indicator.
28383     uploadProgress : function()
28384     {
28385         if (!this.form.progressUrl) {
28386             return;
28387         }
28388         
28389         if (!this.haveProgress) {
28390             Roo.MessageBox.progress("Uploading", "Uploading");
28391         }
28392         if (this.uploadComplete) {
28393            Roo.MessageBox.hide();
28394            return;
28395         }
28396         
28397         this.haveProgress = true;
28398    
28399         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
28400         
28401         var c = new Roo.data.Connection();
28402         c.request({
28403             url : this.form.progressUrl,
28404             params: {
28405                 id : uid
28406             },
28407             method: 'GET',
28408             success : function(req){
28409                //console.log(data);
28410                 var rdata = false;
28411                 var edata;
28412                 try  {
28413                    rdata = Roo.decode(req.responseText)
28414                 } catch (e) {
28415                     Roo.log("Invalid data from server..");
28416                     Roo.log(edata);
28417                     return;
28418                 }
28419                 if (!rdata || !rdata.success) {
28420                     Roo.log(rdata);
28421                     return;
28422                 }
28423                 var data = rdata.data;
28424                 
28425                 if (this.uploadComplete) {
28426                    Roo.MessageBox.hide();
28427                    return;
28428                 }
28429                    
28430                 if (data){
28431                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
28432                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
28433                     );
28434                 }
28435                 this.uploadProgress.defer(2000,this);
28436             },
28437        
28438             failure: function(data) {
28439                 Roo.log('progress url failed ');
28440                 Roo.log(data);
28441             },
28442             scope : this
28443         });
28444            
28445     },
28446     
28447     
28448     run : function()
28449     {
28450         // run get Values on the form, so it syncs any secondary forms.
28451         this.form.getValues();
28452         
28453         var o = this.options;
28454         var method = this.getMethod();
28455         var isPost = method == 'POST';
28456         if(o.clientValidation === false || this.form.isValid()){
28457             
28458             if (this.form.progressUrl) {
28459                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
28460                     (new Date() * 1) + '' + Math.random());
28461                     
28462             } 
28463             
28464             
28465             Roo.Ajax.request(Roo.apply(this.createCallback(), {
28466                 form:this.form.el.dom,
28467                 url:this.getUrl(!isPost),
28468                 method: method,
28469                 params:isPost ? this.getParams() : null,
28470                 isUpload: this.form.fileUpload
28471             }));
28472             
28473             this.uploadProgress();
28474
28475         }else if (o.clientValidation !== false){ // client validation failed
28476             this.failureType = Roo.form.Action.CLIENT_INVALID;
28477             this.form.afterAction(this, false);
28478         }
28479     },
28480
28481     success : function(response)
28482     {
28483         this.uploadComplete= true;
28484         if (this.haveProgress) {
28485             Roo.MessageBox.hide();
28486         }
28487         
28488         
28489         var result = this.processResponse(response);
28490         if(result === true || result.success){
28491             this.form.afterAction(this, true);
28492             return;
28493         }
28494         if(result.errors){
28495             this.form.markInvalid(result.errors);
28496             this.failureType = Roo.form.Action.SERVER_INVALID;
28497         }
28498         this.form.afterAction(this, false);
28499     },
28500     failure : function(response)
28501     {
28502         this.uploadComplete= true;
28503         if (this.haveProgress) {
28504             Roo.MessageBox.hide();
28505         }
28506         
28507         this.response = response;
28508         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28509         this.form.afterAction(this, false);
28510     },
28511     
28512     handleResponse : function(response){
28513         if(this.form.errorReader){
28514             var rs = this.form.errorReader.read(response);
28515             var errors = [];
28516             if(rs.records){
28517                 for(var i = 0, len = rs.records.length; i < len; i++) {
28518                     var r = rs.records[i];
28519                     errors[i] = r.data;
28520                 }
28521             }
28522             if(errors.length < 1){
28523                 errors = null;
28524             }
28525             return {
28526                 success : rs.success,
28527                 errors : errors
28528             };
28529         }
28530         var ret = false;
28531         try {
28532             ret = Roo.decode(response.responseText);
28533         } catch (e) {
28534             ret = {
28535                 success: false,
28536                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
28537                 errors : []
28538             };
28539         }
28540         return ret;
28541         
28542     }
28543 });
28544
28545
28546 Roo.form.Action.Load = function(form, options){
28547     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
28548     this.reader = this.form.reader;
28549 };
28550
28551 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
28552     type : 'load',
28553
28554     run : function(){
28555         
28556         Roo.Ajax.request(Roo.apply(
28557                 this.createCallback(), {
28558                     method:this.getMethod(),
28559                     url:this.getUrl(false),
28560                     params:this.getParams()
28561         }));
28562     },
28563
28564     success : function(response){
28565         
28566         var result = this.processResponse(response);
28567         if(result === true || !result.success || !result.data){
28568             this.failureType = Roo.form.Action.LOAD_FAILURE;
28569             this.form.afterAction(this, false);
28570             return;
28571         }
28572         this.form.clearInvalid();
28573         this.form.setValues(result.data);
28574         this.form.afterAction(this, true);
28575     },
28576
28577     handleResponse : function(response){
28578         if(this.form.reader){
28579             var rs = this.form.reader.read(response);
28580             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
28581             return {
28582                 success : rs.success,
28583                 data : data
28584             };
28585         }
28586         return Roo.decode(response.responseText);
28587     }
28588 });
28589
28590 Roo.form.Action.ACTION_TYPES = {
28591     'load' : Roo.form.Action.Load,
28592     'submit' : Roo.form.Action.Submit
28593 };/*
28594  * Based on:
28595  * Ext JS Library 1.1.1
28596  * Copyright(c) 2006-2007, Ext JS, LLC.
28597  *
28598  * Originally Released Under LGPL - original licence link has changed is not relivant.
28599  *
28600  * Fork - LGPL
28601  * <script type="text/javascript">
28602  */
28603  
28604 /**
28605  * @class Roo.form.Layout
28606  * @extends Roo.Component
28607  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
28608  * @constructor
28609  * @param {Object} config Configuration options
28610  */
28611 Roo.form.Layout = function(config){
28612     var xitems = [];
28613     if (config.items) {
28614         xitems = config.items;
28615         delete config.items;
28616     }
28617     Roo.form.Layout.superclass.constructor.call(this, config);
28618     this.stack = [];
28619     Roo.each(xitems, this.addxtype, this);
28620      
28621 };
28622
28623 Roo.extend(Roo.form.Layout, Roo.Component, {
28624     /**
28625      * @cfg {String/Object} autoCreate
28626      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
28627      */
28628     /**
28629      * @cfg {String/Object/Function} style
28630      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
28631      * a function which returns such a specification.
28632      */
28633     /**
28634      * @cfg {String} labelAlign
28635      * Valid values are "left," "top" and "right" (defaults to "left")
28636      */
28637     /**
28638      * @cfg {Number} labelWidth
28639      * Fixed width in pixels of all field labels (defaults to undefined)
28640      */
28641     /**
28642      * @cfg {Boolean} clear
28643      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
28644      */
28645     clear : true,
28646     /**
28647      * @cfg {String} labelSeparator
28648      * The separator to use after field labels (defaults to ':')
28649      */
28650     labelSeparator : ':',
28651     /**
28652      * @cfg {Boolean} hideLabels
28653      * True to suppress the display of field labels in this layout (defaults to false)
28654      */
28655     hideLabels : false,
28656
28657     // private
28658     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
28659     
28660     isLayout : true,
28661     
28662     // private
28663     onRender : function(ct, position){
28664         if(this.el){ // from markup
28665             this.el = Roo.get(this.el);
28666         }else {  // generate
28667             var cfg = this.getAutoCreate();
28668             this.el = ct.createChild(cfg, position);
28669         }
28670         if(this.style){
28671             this.el.applyStyles(this.style);
28672         }
28673         if(this.labelAlign){
28674             this.el.addClass('x-form-label-'+this.labelAlign);
28675         }
28676         if(this.hideLabels){
28677             this.labelStyle = "display:none";
28678             this.elementStyle = "padding-left:0;";
28679         }else{
28680             if(typeof this.labelWidth == 'number'){
28681                 this.labelStyle = "width:"+this.labelWidth+"px;";
28682                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
28683             }
28684             if(this.labelAlign == 'top'){
28685                 this.labelStyle = "width:auto;";
28686                 this.elementStyle = "padding-left:0;";
28687             }
28688         }
28689         var stack = this.stack;
28690         var slen = stack.length;
28691         if(slen > 0){
28692             if(!this.fieldTpl){
28693                 var t = new Roo.Template(
28694                     '<div class="x-form-item {5}">',
28695                         '<label for="{0}" style="{2}">{1}{4}</label>',
28696                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28697                         '</div>',
28698                     '</div><div class="x-form-clear-left"></div>'
28699                 );
28700                 t.disableFormats = true;
28701                 t.compile();
28702                 Roo.form.Layout.prototype.fieldTpl = t;
28703             }
28704             for(var i = 0; i < slen; i++) {
28705                 if(stack[i].isFormField){
28706                     this.renderField(stack[i]);
28707                 }else{
28708                     this.renderComponent(stack[i]);
28709                 }
28710             }
28711         }
28712         if(this.clear){
28713             this.el.createChild({cls:'x-form-clear'});
28714         }
28715     },
28716
28717     // private
28718     renderField : function(f){
28719         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
28720                f.id, //0
28721                f.fieldLabel, //1
28722                f.labelStyle||this.labelStyle||'', //2
28723                this.elementStyle||'', //3
28724                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
28725                f.itemCls||this.itemCls||''  //5
28726        ], true).getPrevSibling());
28727     },
28728
28729     // private
28730     renderComponent : function(c){
28731         c.render(c.isLayout ? this.el : this.el.createChild());    
28732     },
28733     /**
28734      * Adds a object form elements (using the xtype property as the factory method.)
28735      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
28736      * @param {Object} config 
28737      */
28738     addxtype : function(o)
28739     {
28740         // create the lement.
28741         o.form = this.form;
28742         var fe = Roo.factory(o, Roo.form);
28743         this.form.allItems.push(fe);
28744         this.stack.push(fe);
28745         
28746         if (fe.isFormField) {
28747             this.form.items.add(fe);
28748         }
28749          
28750         return fe;
28751     }
28752 });
28753
28754 /**
28755  * @class Roo.form.Column
28756  * @extends Roo.form.Layout
28757  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
28758  * @constructor
28759  * @param {Object} config Configuration options
28760  */
28761 Roo.form.Column = function(config){
28762     Roo.form.Column.superclass.constructor.call(this, config);
28763 };
28764
28765 Roo.extend(Roo.form.Column, Roo.form.Layout, {
28766     /**
28767      * @cfg {Number/String} width
28768      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28769      */
28770     /**
28771      * @cfg {String/Object} autoCreate
28772      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
28773      */
28774
28775     // private
28776     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
28777
28778     // private
28779     onRender : function(ct, position){
28780         Roo.form.Column.superclass.onRender.call(this, ct, position);
28781         if(this.width){
28782             this.el.setWidth(this.width);
28783         }
28784     }
28785 });
28786
28787
28788 /**
28789  * @class Roo.form.Row
28790  * @extends Roo.form.Layout
28791  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
28792  * @constructor
28793  * @param {Object} config Configuration options
28794  */
28795
28796  
28797 Roo.form.Row = function(config){
28798     Roo.form.Row.superclass.constructor.call(this, config);
28799 };
28800  
28801 Roo.extend(Roo.form.Row, Roo.form.Layout, {
28802       /**
28803      * @cfg {Number/String} width
28804      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28805      */
28806     /**
28807      * @cfg {Number/String} height
28808      * The fixed height of the column in pixels or CSS value (defaults to "auto")
28809      */
28810     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
28811     
28812     padWidth : 20,
28813     // private
28814     onRender : function(ct, position){
28815         //console.log('row render');
28816         if(!this.rowTpl){
28817             var t = new Roo.Template(
28818                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
28819                     '<label for="{0}" style="{2}">{1}{4}</label>',
28820                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28821                     '</div>',
28822                 '</div>'
28823             );
28824             t.disableFormats = true;
28825             t.compile();
28826             Roo.form.Layout.prototype.rowTpl = t;
28827         }
28828         this.fieldTpl = this.rowTpl;
28829         
28830         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
28831         var labelWidth = 100;
28832         
28833         if ((this.labelAlign != 'top')) {
28834             if (typeof this.labelWidth == 'number') {
28835                 labelWidth = this.labelWidth
28836             }
28837             this.padWidth =  20 + labelWidth;
28838             
28839         }
28840         
28841         Roo.form.Column.superclass.onRender.call(this, ct, position);
28842         if(this.width){
28843             this.el.setWidth(this.width);
28844         }
28845         if(this.height){
28846             this.el.setHeight(this.height);
28847         }
28848     },
28849     
28850     // private
28851     renderField : function(f){
28852         f.fieldEl = this.fieldTpl.append(this.el, [
28853                f.id, f.fieldLabel,
28854                f.labelStyle||this.labelStyle||'',
28855                this.elementStyle||'',
28856                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
28857                f.itemCls||this.itemCls||'',
28858                f.width ? f.width + this.padWidth : 160 + this.padWidth
28859        ],true);
28860     }
28861 });
28862  
28863
28864 /**
28865  * @class Roo.form.FieldSet
28866  * @extends Roo.form.Layout
28867  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
28868  * @constructor
28869  * @param {Object} config Configuration options
28870  */
28871 Roo.form.FieldSet = function(config){
28872     Roo.form.FieldSet.superclass.constructor.call(this, config);
28873 };
28874
28875 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
28876     /**
28877      * @cfg {String} legend
28878      * The text to display as the legend for the FieldSet (defaults to '')
28879      */
28880     /**
28881      * @cfg {String/Object} autoCreate
28882      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
28883      */
28884
28885     // private
28886     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
28887
28888     // private
28889     onRender : function(ct, position){
28890         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
28891         if(this.legend){
28892             this.setLegend(this.legend);
28893         }
28894     },
28895
28896     // private
28897     setLegend : function(text){
28898         if(this.rendered){
28899             this.el.child('legend').update(text);
28900         }
28901     }
28902 });/*
28903  * Based on:
28904  * Ext JS Library 1.1.1
28905  * Copyright(c) 2006-2007, Ext JS, LLC.
28906  *
28907  * Originally Released Under LGPL - original licence link has changed is not relivant.
28908  *
28909  * Fork - LGPL
28910  * <script type="text/javascript">
28911  */
28912 /**
28913  * @class Roo.form.VTypes
28914  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
28915  * @singleton
28916  */
28917 Roo.form.VTypes = function(){
28918     // closure these in so they are only created once.
28919     var alpha = /^[a-zA-Z_]+$/;
28920     var alphanum = /^[a-zA-Z0-9_]+$/;
28921     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
28922     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
28923
28924     // All these messages and functions are configurable
28925     return {
28926         /**
28927          * The function used to validate email addresses
28928          * @param {String} value The email address
28929          */
28930         'email' : function(v){
28931             return email.test(v);
28932         },
28933         /**
28934          * The error text to display when the email validation function returns false
28935          * @type String
28936          */
28937         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
28938         /**
28939          * The keystroke filter mask to be applied on email input
28940          * @type RegExp
28941          */
28942         'emailMask' : /[a-z0-9_\.\-@]/i,
28943
28944         /**
28945          * The function used to validate URLs
28946          * @param {String} value The URL
28947          */
28948         'url' : function(v){
28949             return url.test(v);
28950         },
28951         /**
28952          * The error text to display when the url validation function returns false
28953          * @type String
28954          */
28955         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
28956         
28957         /**
28958          * The function used to validate alpha values
28959          * @param {String} value The value
28960          */
28961         'alpha' : function(v){
28962             return alpha.test(v);
28963         },
28964         /**
28965          * The error text to display when the alpha validation function returns false
28966          * @type String
28967          */
28968         'alphaText' : 'This field should only contain letters and _',
28969         /**
28970          * The keystroke filter mask to be applied on alpha input
28971          * @type RegExp
28972          */
28973         'alphaMask' : /[a-z_]/i,
28974
28975         /**
28976          * The function used to validate alphanumeric values
28977          * @param {String} value The value
28978          */
28979         'alphanum' : function(v){
28980             return alphanum.test(v);
28981         },
28982         /**
28983          * The error text to display when the alphanumeric validation function returns false
28984          * @type String
28985          */
28986         'alphanumText' : 'This field should only contain letters, numbers and _',
28987         /**
28988          * The keystroke filter mask to be applied on alphanumeric input
28989          * @type RegExp
28990          */
28991         'alphanumMask' : /[a-z0-9_]/i
28992     };
28993 }();//<script type="text/javascript">
28994
28995 /**
28996  * @class Roo.form.FCKeditor
28997  * @extends Roo.form.TextArea
28998  * Wrapper around the FCKEditor http://www.fckeditor.net
28999  * @constructor
29000  * Creates a new FCKeditor
29001  * @param {Object} config Configuration options
29002  */
29003 Roo.form.FCKeditor = function(config){
29004     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29005     this.addEvents({
29006          /**
29007          * @event editorinit
29008          * Fired when the editor is initialized - you can add extra handlers here..
29009          * @param {FCKeditor} this
29010          * @param {Object} the FCK object.
29011          */
29012         editorinit : true
29013     });
29014     
29015     
29016 };
29017 Roo.form.FCKeditor.editors = { };
29018 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29019 {
29020     //defaultAutoCreate : {
29021     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29022     //},
29023     // private
29024     /**
29025      * @cfg {Object} fck options - see fck manual for details.
29026      */
29027     fckconfig : false,
29028     
29029     /**
29030      * @cfg {Object} fck toolbar set (Basic or Default)
29031      */
29032     toolbarSet : 'Basic',
29033     /**
29034      * @cfg {Object} fck BasePath
29035      */ 
29036     basePath : '/fckeditor/',
29037     
29038     
29039     frame : false,
29040     
29041     value : '',
29042     
29043    
29044     onRender : function(ct, position)
29045     {
29046         if(!this.el){
29047             this.defaultAutoCreate = {
29048                 tag: "textarea",
29049                 style:"width:300px;height:60px;",
29050                 autocomplete: "off"
29051             };
29052         }
29053         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29054         /*
29055         if(this.grow){
29056             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29057             if(this.preventScrollbars){
29058                 this.el.setStyle("overflow", "hidden");
29059             }
29060             this.el.setHeight(this.growMin);
29061         }
29062         */
29063         //console.log('onrender' + this.getId() );
29064         Roo.form.FCKeditor.editors[this.getId()] = this;
29065          
29066
29067         this.replaceTextarea() ;
29068         
29069     },
29070     
29071     getEditor : function() {
29072         return this.fckEditor;
29073     },
29074     /**
29075      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29076      * @param {Mixed} value The value to set
29077      */
29078     
29079     
29080     setValue : function(value)
29081     {
29082         //console.log('setValue: ' + value);
29083         
29084         if(typeof(value) == 'undefined') { // not sure why this is happending...
29085             return;
29086         }
29087         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29088         
29089         //if(!this.el || !this.getEditor()) {
29090         //    this.value = value;
29091             //this.setValue.defer(100,this,[value]);    
29092         //    return;
29093         //} 
29094         
29095         if(!this.getEditor()) {
29096             return;
29097         }
29098         
29099         this.getEditor().SetData(value);
29100         
29101         //
29102
29103     },
29104
29105     /**
29106      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29107      * @return {Mixed} value The field value
29108      */
29109     getValue : function()
29110     {
29111         
29112         if (this.frame && this.frame.dom.style.display == 'none') {
29113             return Roo.form.FCKeditor.superclass.getValue.call(this);
29114         }
29115         
29116         if(!this.el || !this.getEditor()) {
29117            
29118            // this.getValue.defer(100,this); 
29119             return this.value;
29120         }
29121        
29122         
29123         var value=this.getEditor().GetData();
29124         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29125         return Roo.form.FCKeditor.superclass.getValue.call(this);
29126         
29127
29128     },
29129
29130     /**
29131      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
29132      * @return {Mixed} value The field value
29133      */
29134     getRawValue : function()
29135     {
29136         if (this.frame && this.frame.dom.style.display == 'none') {
29137             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29138         }
29139         
29140         if(!this.el || !this.getEditor()) {
29141             //this.getRawValue.defer(100,this); 
29142             return this.value;
29143             return;
29144         }
29145         
29146         
29147         
29148         var value=this.getEditor().GetData();
29149         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29150         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29151          
29152     },
29153     
29154     setSize : function(w,h) {
29155         
29156         
29157         
29158         //if (this.frame && this.frame.dom.style.display == 'none') {
29159         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29160         //    return;
29161         //}
29162         //if(!this.el || !this.getEditor()) {
29163         //    this.setSize.defer(100,this, [w,h]); 
29164         //    return;
29165         //}
29166         
29167         
29168         
29169         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29170         
29171         this.frame.dom.setAttribute('width', w);
29172         this.frame.dom.setAttribute('height', h);
29173         this.frame.setSize(w,h);
29174         
29175     },
29176     
29177     toggleSourceEdit : function(value) {
29178         
29179       
29180          
29181         this.el.dom.style.display = value ? '' : 'none';
29182         this.frame.dom.style.display = value ?  'none' : '';
29183         
29184     },
29185     
29186     
29187     focus: function(tag)
29188     {
29189         if (this.frame.dom.style.display == 'none') {
29190             return Roo.form.FCKeditor.superclass.focus.call(this);
29191         }
29192         if(!this.el || !this.getEditor()) {
29193             this.focus.defer(100,this, [tag]); 
29194             return;
29195         }
29196         
29197         
29198         
29199         
29200         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
29201         this.getEditor().Focus();
29202         if (tgs.length) {
29203             if (!this.getEditor().Selection.GetSelection()) {
29204                 this.focus.defer(100,this, [tag]); 
29205                 return;
29206             }
29207             
29208             
29209             var r = this.getEditor().EditorDocument.createRange();
29210             r.setStart(tgs[0],0);
29211             r.setEnd(tgs[0],0);
29212             this.getEditor().Selection.GetSelection().removeAllRanges();
29213             this.getEditor().Selection.GetSelection().addRange(r);
29214             this.getEditor().Focus();
29215         }
29216         
29217     },
29218     
29219     
29220     
29221     replaceTextarea : function()
29222     {
29223         if ( document.getElementById( this.getId() + '___Frame' ) )
29224             return ;
29225         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
29226         //{
29227             // We must check the elements firstly using the Id and then the name.
29228         var oTextarea = document.getElementById( this.getId() );
29229         
29230         var colElementsByName = document.getElementsByName( this.getId() ) ;
29231          
29232         oTextarea.style.display = 'none' ;
29233
29234         if ( oTextarea.tabIndex ) {            
29235             this.TabIndex = oTextarea.tabIndex ;
29236         }
29237         
29238         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
29239         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
29240         this.frame = Roo.get(this.getId() + '___Frame')
29241     },
29242     
29243     _getConfigHtml : function()
29244     {
29245         var sConfig = '' ;
29246
29247         for ( var o in this.fckconfig ) {
29248             sConfig += sConfig.length > 0  ? '&amp;' : '';
29249             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
29250         }
29251
29252         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
29253     },
29254     
29255     
29256     _getIFrameHtml : function()
29257     {
29258         var sFile = 'fckeditor.html' ;
29259         /* no idea what this is about..
29260         try
29261         {
29262             if ( (/fcksource=true/i).test( window.top.location.search ) )
29263                 sFile = 'fckeditor.original.html' ;
29264         }
29265         catch (e) { 
29266         */
29267
29268         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
29269         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
29270         
29271         
29272         var html = '<iframe id="' + this.getId() +
29273             '___Frame" src="' + sLink +
29274             '" width="' + this.width +
29275             '" height="' + this.height + '"' +
29276             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
29277             ' frameborder="0" scrolling="no"></iframe>' ;
29278
29279         return html ;
29280     },
29281     
29282     _insertHtmlBefore : function( html, element )
29283     {
29284         if ( element.insertAdjacentHTML )       {
29285             // IE
29286             element.insertAdjacentHTML( 'beforeBegin', html ) ;
29287         } else { // Gecko
29288             var oRange = document.createRange() ;
29289             oRange.setStartBefore( element ) ;
29290             var oFragment = oRange.createContextualFragment( html );
29291             element.parentNode.insertBefore( oFragment, element ) ;
29292         }
29293     }
29294     
29295     
29296   
29297     
29298     
29299     
29300     
29301
29302 });
29303
29304 //Roo.reg('fckeditor', Roo.form.FCKeditor);
29305
29306 function FCKeditor_OnComplete(editorInstance){
29307     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
29308     f.fckEditor = editorInstance;
29309     //console.log("loaded");
29310     f.fireEvent('editorinit', f, editorInstance);
29311
29312   
29313
29314  
29315
29316
29317
29318
29319
29320
29321
29322
29323
29324
29325
29326
29327
29328
29329
29330 //<script type="text/javascript">
29331 /**
29332  * @class Roo.form.GridField
29333  * @extends Roo.form.Field
29334  * Embed a grid (or editable grid into a form)
29335  * STATUS ALPHA
29336  * 
29337  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
29338  * it needs 
29339  * xgrid.store = Roo.data.Store
29340  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
29341  * xgrid.store.reader = Roo.data.JsonReader 
29342  * 
29343  * 
29344  * @constructor
29345  * Creates a new GridField
29346  * @param {Object} config Configuration options
29347  */
29348 Roo.form.GridField = function(config){
29349     Roo.form.GridField.superclass.constructor.call(this, config);
29350      
29351 };
29352
29353 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
29354     /**
29355      * @cfg {Number} width  - used to restrict width of grid..
29356      */
29357     width : 100,
29358     /**
29359      * @cfg {Number} height - used to restrict height of grid..
29360      */
29361     height : 50,
29362      /**
29363      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
29364          * 
29365          *}
29366      */
29367     xgrid : false, 
29368     /**
29369      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29370      * {tag: "input", type: "checkbox", autocomplete: "off"})
29371      */
29372    // defaultAutoCreate : { tag: 'div' },
29373     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29374     /**
29375      * @cfg {String} addTitle Text to include for adding a title.
29376      */
29377     addTitle : false,
29378     //
29379     onResize : function(){
29380         Roo.form.Field.superclass.onResize.apply(this, arguments);
29381     },
29382
29383     initEvents : function(){
29384         // Roo.form.Checkbox.superclass.initEvents.call(this);
29385         // has no events...
29386        
29387     },
29388
29389
29390     getResizeEl : function(){
29391         return this.wrap;
29392     },
29393
29394     getPositionEl : function(){
29395         return this.wrap;
29396     },
29397
29398     // private
29399     onRender : function(ct, position){
29400         
29401         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
29402         var style = this.style;
29403         delete this.style;
29404         
29405         Roo.form.GridField.superclass.onRender.call(this, ct, position);
29406         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
29407         this.viewEl = this.wrap.createChild({ tag: 'div' });
29408         if (style) {
29409             this.viewEl.applyStyles(style);
29410         }
29411         if (this.width) {
29412             this.viewEl.setWidth(this.width);
29413         }
29414         if (this.height) {
29415             this.viewEl.setHeight(this.height);
29416         }
29417         //if(this.inputValue !== undefined){
29418         //this.setValue(this.value);
29419         
29420         
29421         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
29422         
29423         
29424         this.grid.render();
29425         this.grid.getDataSource().on('remove', this.refreshValue, this);
29426         this.grid.getDataSource().on('update', this.refreshValue, this);
29427         this.grid.on('afteredit', this.refreshValue, this);
29428  
29429     },
29430      
29431     
29432     /**
29433      * Sets the value of the item. 
29434      * @param {String} either an object  or a string..
29435      */
29436     setValue : function(v){
29437         //this.value = v;
29438         v = v || []; // empty set..
29439         // this does not seem smart - it really only affects memoryproxy grids..
29440         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
29441             var ds = this.grid.getDataSource();
29442             // assumes a json reader..
29443             var data = {}
29444             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
29445             ds.loadData( data);
29446         }
29447         // clear selection so it does not get stale.
29448         if (this.grid.sm) { 
29449             this.grid.sm.clearSelections();
29450         }
29451         
29452         Roo.form.GridField.superclass.setValue.call(this, v);
29453         this.refreshValue();
29454         // should load data in the grid really....
29455     },
29456     
29457     // private
29458     refreshValue: function() {
29459          var val = [];
29460         this.grid.getDataSource().each(function(r) {
29461             val.push(r.data);
29462         });
29463         this.el.dom.value = Roo.encode(val);
29464     }
29465     
29466      
29467     
29468     
29469 });/*
29470  * Based on:
29471  * Ext JS Library 1.1.1
29472  * Copyright(c) 2006-2007, Ext JS, LLC.
29473  *
29474  * Originally Released Under LGPL - original licence link has changed is not relivant.
29475  *
29476  * Fork - LGPL
29477  * <script type="text/javascript">
29478  */
29479 /**
29480  * @class Roo.form.DisplayField
29481  * @extends Roo.form.Field
29482  * A generic Field to display non-editable data.
29483  * @constructor
29484  * Creates a new Display Field item.
29485  * @param {Object} config Configuration options
29486  */
29487 Roo.form.DisplayField = function(config){
29488     Roo.form.DisplayField.superclass.constructor.call(this, config);
29489     
29490 };
29491
29492 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
29493     inputType:      'hidden',
29494     allowBlank:     true,
29495     readOnly:         true,
29496     
29497  
29498     /**
29499      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29500      */
29501     focusClass : undefined,
29502     /**
29503      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29504      */
29505     fieldClass: 'x-form-field',
29506     
29507      /**
29508      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
29509      */
29510     valueRenderer: undefined,
29511     
29512     width: 100,
29513     /**
29514      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29515      * {tag: "input", type: "checkbox", autocomplete: "off"})
29516      */
29517      
29518  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29519
29520     onResize : function(){
29521         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
29522         
29523     },
29524
29525     initEvents : function(){
29526         // Roo.form.Checkbox.superclass.initEvents.call(this);
29527         // has no events...
29528        
29529     },
29530
29531
29532     getResizeEl : function(){
29533         return this.wrap;
29534     },
29535
29536     getPositionEl : function(){
29537         return this.wrap;
29538     },
29539
29540     // private
29541     onRender : function(ct, position){
29542         
29543         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
29544         //if(this.inputValue !== undefined){
29545         this.wrap = this.el.wrap();
29546         
29547         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
29548         
29549         if (this.bodyStyle) {
29550             this.viewEl.applyStyles(this.bodyStyle);
29551         }
29552         //this.viewEl.setStyle('padding', '2px');
29553         
29554         this.setValue(this.value);
29555         
29556     },
29557 /*
29558     // private
29559     initValue : Roo.emptyFn,
29560
29561   */
29562
29563         // private
29564     onClick : function(){
29565         
29566     },
29567
29568     /**
29569      * Sets the checked state of the checkbox.
29570      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
29571      */
29572     setValue : function(v){
29573         this.value = v;
29574         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
29575         // this might be called before we have a dom element..
29576         if (!this.viewEl) {
29577             return;
29578         }
29579         this.viewEl.dom.innerHTML = html;
29580         Roo.form.DisplayField.superclass.setValue.call(this, v);
29581
29582     }
29583 });/*
29584  * 
29585  * Licence- LGPL
29586  * 
29587  */
29588
29589 /**
29590  * @class Roo.form.DayPicker
29591  * @extends Roo.form.Field
29592  * A Day picker show [M] [T] [W] ....
29593  * @constructor
29594  * Creates a new Day Picker
29595  * @param {Object} config Configuration options
29596  */
29597 Roo.form.DayPicker= function(config){
29598     Roo.form.DayPicker.superclass.constructor.call(this, config);
29599      
29600 };
29601
29602 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
29603     /**
29604      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29605      */
29606     focusClass : undefined,
29607     /**
29608      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29609      */
29610     fieldClass: "x-form-field",
29611    
29612     /**
29613      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29614      * {tag: "input", type: "checkbox", autocomplete: "off"})
29615      */
29616     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
29617     
29618    
29619     actionMode : 'viewEl', 
29620     //
29621     // private
29622  
29623     inputType : 'hidden',
29624     
29625      
29626     inputElement: false, // real input element?
29627     basedOn: false, // ????
29628     
29629     isFormField: true, // not sure where this is needed!!!!
29630
29631     onResize : function(){
29632         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
29633         if(!this.boxLabel){
29634             this.el.alignTo(this.wrap, 'c-c');
29635         }
29636     },
29637
29638     initEvents : function(){
29639         Roo.form.Checkbox.superclass.initEvents.call(this);
29640         this.el.on("click", this.onClick,  this);
29641         this.el.on("change", this.onClick,  this);
29642     },
29643
29644
29645     getResizeEl : function(){
29646         return this.wrap;
29647     },
29648
29649     getPositionEl : function(){
29650         return this.wrap;
29651     },
29652
29653     
29654     // private
29655     onRender : function(ct, position){
29656         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
29657        
29658         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
29659         
29660         var r1 = '<table><tr>';
29661         var r2 = '<tr class="x-form-daypick-icons">';
29662         for (var i=0; i < 7; i++) {
29663             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
29664             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
29665         }
29666         
29667         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
29668         viewEl.select('img').on('click', this.onClick, this);
29669         this.viewEl = viewEl;   
29670         
29671         
29672         // this will not work on Chrome!!!
29673         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
29674         this.el.on('propertychange', this.setFromHidden,  this);  //ie
29675         
29676         
29677           
29678
29679     },
29680
29681     // private
29682     initValue : Roo.emptyFn,
29683
29684     /**
29685      * Returns the checked state of the checkbox.
29686      * @return {Boolean} True if checked, else false
29687      */
29688     getValue : function(){
29689         return this.el.dom.value;
29690         
29691     },
29692
29693         // private
29694     onClick : function(e){ 
29695         //this.setChecked(!this.checked);
29696         Roo.get(e.target).toggleClass('x-menu-item-checked');
29697         this.refreshValue();
29698         //if(this.el.dom.checked != this.checked){
29699         //    this.setValue(this.el.dom.checked);
29700        // }
29701     },
29702     
29703     // private
29704     refreshValue : function()
29705     {
29706         var val = '';
29707         this.viewEl.select('img',true).each(function(e,i,n)  {
29708             val += e.is(".x-menu-item-checked") ? String(n) : '';
29709         });
29710         this.setValue(val, true);
29711     },
29712
29713     /**
29714      * Sets the checked state of the checkbox.
29715      * On is always based on a string comparison between inputValue and the param.
29716      * @param {Boolean/String} value - the value to set 
29717      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
29718      */
29719     setValue : function(v,suppressEvent){
29720         if (!this.el.dom) {
29721             return;
29722         }
29723         var old = this.el.dom.value ;
29724         this.el.dom.value = v;
29725         if (suppressEvent) {
29726             return ;
29727         }
29728          
29729         // update display..
29730         this.viewEl.select('img',true).each(function(e,i,n)  {
29731             
29732             var on = e.is(".x-menu-item-checked");
29733             var newv = v.indexOf(String(n)) > -1;
29734             if (on != newv) {
29735                 e.toggleClass('x-menu-item-checked');
29736             }
29737             
29738         });
29739         
29740         
29741         this.fireEvent('change', this, v, old);
29742         
29743         
29744     },
29745    
29746     // handle setting of hidden value by some other method!!?!?
29747     setFromHidden: function()
29748     {
29749         if(!this.el){
29750             return;
29751         }
29752         //console.log("SET FROM HIDDEN");
29753         //alert('setFrom hidden');
29754         this.setValue(this.el.dom.value);
29755     },
29756     
29757     onDestroy : function()
29758     {
29759         if(this.viewEl){
29760             Roo.get(this.viewEl).remove();
29761         }
29762          
29763         Roo.form.DayPicker.superclass.onDestroy.call(this);
29764     }
29765
29766 });/*
29767  * RooJS Library 1.1.1
29768  * Copyright(c) 2008-2011  Alan Knowles
29769  *
29770  * License - LGPL
29771  */
29772  
29773
29774 /**
29775  * @class Roo.form.ComboCheck
29776  * @extends Roo.form.ComboBox
29777  * A combobox for multiple select items.
29778  *
29779  * FIXME - could do with a reset button..
29780  * 
29781  * @constructor
29782  * Create a new ComboCheck
29783  * @param {Object} config Configuration options
29784  */
29785 Roo.form.ComboCheck = function(config){
29786     Roo.form.ComboCheck.superclass.constructor.call(this, config);
29787     // should verify some data...
29788     // like
29789     // hiddenName = required..
29790     // displayField = required
29791     // valudField == required
29792     var req= [ 'hiddenName', 'displayField', 'valueField' ];
29793     var _t = this;
29794     Roo.each(req, function(e) {
29795         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
29796             throw "Roo.form.ComboCheck : missing value for: " + e;
29797         }
29798     });
29799     
29800     
29801 };
29802
29803 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
29804      
29805      
29806     editable : false,
29807      
29808     selectedClass: 'x-menu-item-checked', 
29809     
29810     // private
29811     onRender : function(ct, position){
29812         var _t = this;
29813         
29814         
29815         
29816         if(!this.tpl){
29817             var cls = 'x-combo-list';
29818
29819             
29820             this.tpl =  new Roo.Template({
29821                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
29822                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
29823                    '<span>{' + this.displayField + '}</span>' +
29824                     '</div>' 
29825                 
29826             });
29827         }
29828  
29829         
29830         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
29831         this.view.singleSelect = false;
29832         this.view.multiSelect = true;
29833         this.view.toggleSelect = true;
29834         this.pageTb.add(new Roo.Toolbar.Fill(), {
29835             
29836             text: 'Done',
29837             handler: function()
29838             {
29839                 _t.collapse();
29840             }
29841         });
29842     },
29843     
29844     onViewOver : function(e, t){
29845         // do nothing...
29846         return;
29847         
29848     },
29849     
29850     onViewClick : function(doFocus,index){
29851         return;
29852         
29853     },
29854     select: function () {
29855         //Roo.log("SELECT CALLED");
29856     },
29857      
29858     selectByValue : function(xv, scrollIntoView){
29859         var ar = this.getValueArray();
29860         var sels = [];
29861         
29862         Roo.each(ar, function(v) {
29863             if(v === undefined || v === null){
29864                 return;
29865             }
29866             var r = this.findRecord(this.valueField, v);
29867             if(r){
29868                 sels.push(this.store.indexOf(r))
29869                 
29870             }
29871         },this);
29872         this.view.select(sels);
29873         return false;
29874     },
29875     
29876     
29877     
29878     onSelect : function(record, index){
29879        // Roo.log("onselect Called");
29880        // this is only called by the clear button now..
29881         this.view.clearSelections();
29882         this.setValue('[]');
29883         if (this.value != this.valueBefore) {
29884             this.fireEvent('change', this, this.value, this.valueBefore);
29885         }
29886     },
29887     getValueArray : function()
29888     {
29889         var ar = [] ;
29890         
29891         try {
29892             //Roo.log(this.value);
29893             if (typeof(this.value) == 'undefined') {
29894                 return [];
29895             }
29896             var ar = Roo.decode(this.value);
29897             return  ar instanceof Array ? ar : []; //?? valid?
29898             
29899         } catch(e) {
29900             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
29901             return [];
29902         }
29903          
29904     },
29905     expand : function ()
29906     {
29907         Roo.form.ComboCheck.superclass.expand.call(this);
29908         this.valueBefore = this.value;
29909         
29910
29911     },
29912     
29913     collapse : function(){
29914         Roo.form.ComboCheck.superclass.collapse.call(this);
29915         var sl = this.view.getSelectedIndexes();
29916         var st = this.store;
29917         var nv = [];
29918         var tv = [];
29919         var r;
29920         Roo.each(sl, function(i) {
29921             r = st.getAt(i);
29922             nv.push(r.get(this.valueField));
29923         },this);
29924         this.setValue(Roo.encode(nv));
29925         if (this.value != this.valueBefore) {
29926
29927             this.fireEvent('change', this, this.value, this.valueBefore);
29928         }
29929         
29930     },
29931     
29932     setValue : function(v){
29933         // Roo.log(v);
29934         this.value = v;
29935         
29936         var vals = this.getValueArray();
29937         var tv = [];
29938         Roo.each(vals, function(k) {
29939             var r = this.findRecord(this.valueField, k);
29940             if(r){
29941                 tv.push(r.data[this.displayField]);
29942             }else if(this.valueNotFoundText !== undefined){
29943                 tv.push( this.valueNotFoundText );
29944             }
29945         },this);
29946        // Roo.log(tv);
29947         
29948         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
29949         this.hiddenField.value = v;
29950         this.value = v;
29951     }
29952     
29953 });//<script type="text/javasscript">
29954  
29955
29956 /**
29957  * @class Roo.DDView
29958  * A DnD enabled version of Roo.View.
29959  * @param {Element/String} container The Element in which to create the View.
29960  * @param {String} tpl The template string used to create the markup for each element of the View
29961  * @param {Object} config The configuration properties. These include all the config options of
29962  * {@link Roo.View} plus some specific to this class.<br>
29963  * <p>
29964  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
29965  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
29966  * <p>
29967  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
29968 .x-view-drag-insert-above {
29969         border-top:1px dotted #3366cc;
29970 }
29971 .x-view-drag-insert-below {
29972         border-bottom:1px dotted #3366cc;
29973 }
29974 </code></pre>
29975  * 
29976  */
29977  
29978 Roo.DDView = function(container, tpl, config) {
29979     Roo.DDView.superclass.constructor.apply(this, arguments);
29980     this.getEl().setStyle("outline", "0px none");
29981     this.getEl().unselectable();
29982     if (this.dragGroup) {
29983                 this.setDraggable(this.dragGroup.split(","));
29984     }
29985     if (this.dropGroup) {
29986                 this.setDroppable(this.dropGroup.split(","));
29987     }
29988     if (this.deletable) {
29989         this.setDeletable();
29990     }
29991     this.isDirtyFlag = false;
29992         this.addEvents({
29993                 "drop" : true
29994         });
29995 };
29996
29997 Roo.extend(Roo.DDView, Roo.View, {
29998 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
29999 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
30000 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
30001 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
30002
30003         isFormField: true,
30004
30005         reset: Roo.emptyFn,
30006         
30007         clearInvalid: Roo.form.Field.prototype.clearInvalid,
30008
30009         validate: function() {
30010                 return true;
30011         },
30012         
30013         destroy: function() {
30014                 this.purgeListeners();
30015                 this.getEl.removeAllListeners();
30016                 this.getEl().remove();
30017                 if (this.dragZone) {
30018                         if (this.dragZone.destroy) {
30019                                 this.dragZone.destroy();
30020                         }
30021                 }
30022                 if (this.dropZone) {
30023                         if (this.dropZone.destroy) {
30024                                 this.dropZone.destroy();
30025                         }
30026                 }
30027         },
30028
30029 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
30030         getName: function() {
30031                 return this.name;
30032         },
30033
30034 /**     Loads the View from a JSON string representing the Records to put into the Store. */
30035         setValue: function(v) {
30036                 if (!this.store) {
30037                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
30038                 }
30039                 var data = {};
30040                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
30041                 this.store.proxy = new Roo.data.MemoryProxy(data);
30042                 this.store.load();
30043         },
30044
30045 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
30046         getValue: function() {
30047                 var result = '(';
30048                 this.store.each(function(rec) {
30049                         result += rec.id + ',';
30050                 });
30051                 return result.substr(0, result.length - 1) + ')';
30052         },
30053         
30054         getIds: function() {
30055                 var i = 0, result = new Array(this.store.getCount());
30056                 this.store.each(function(rec) {
30057                         result[i++] = rec.id;
30058                 });
30059                 return result;
30060         },
30061         
30062         isDirty: function() {
30063                 return this.isDirtyFlag;
30064         },
30065
30066 /**
30067  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
30068  *      whole Element becomes the target, and this causes the drop gesture to append.
30069  */
30070     getTargetFromEvent : function(e) {
30071                 var target = e.getTarget();
30072                 while ((target !== null) && (target.parentNode != this.el.dom)) {
30073                 target = target.parentNode;
30074                 }
30075                 if (!target) {
30076                         target = this.el.dom.lastChild || this.el.dom;
30077                 }
30078                 return target;
30079     },
30080
30081 /**
30082  *      Create the drag data which consists of an object which has the property "ddel" as
30083  *      the drag proxy element. 
30084  */
30085     getDragData : function(e) {
30086         var target = this.findItemFromChild(e.getTarget());
30087                 if(target) {
30088                         this.handleSelection(e);
30089                         var selNodes = this.getSelectedNodes();
30090             var dragData = {
30091                 source: this,
30092                 copy: this.copy || (this.allowCopy && e.ctrlKey),
30093                 nodes: selNodes,
30094                 records: []
30095                         };
30096                         var selectedIndices = this.getSelectedIndexes();
30097                         for (var i = 0; i < selectedIndices.length; i++) {
30098                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
30099                         }
30100                         if (selNodes.length == 1) {
30101                                 dragData.ddel = target.cloneNode(true); // the div element
30102                         } else {
30103                                 var div = document.createElement('div'); // create the multi element drag "ghost"
30104                                 div.className = 'multi-proxy';
30105                                 for (var i = 0, len = selNodes.length; i < len; i++) {
30106                                         div.appendChild(selNodes[i].cloneNode(true));
30107                                 }
30108                                 dragData.ddel = div;
30109                         }
30110             //console.log(dragData)
30111             //console.log(dragData.ddel.innerHTML)
30112                         return dragData;
30113                 }
30114         //console.log('nodragData')
30115                 return false;
30116     },
30117     
30118 /**     Specify to which ddGroup items in this DDView may be dragged. */
30119     setDraggable: function(ddGroup) {
30120         if (ddGroup instanceof Array) {
30121                 Roo.each(ddGroup, this.setDraggable, this);
30122                 return;
30123         }
30124         if (this.dragZone) {
30125                 this.dragZone.addToGroup(ddGroup);
30126         } else {
30127                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
30128                                 containerScroll: true,
30129                                 ddGroup: ddGroup 
30130
30131                         });
30132 //                      Draggability implies selection. DragZone's mousedown selects the element.
30133                         if (!this.multiSelect) { this.singleSelect = true; }
30134
30135 //                      Wire the DragZone's handlers up to methods in *this*
30136                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
30137                 }
30138     },
30139
30140 /**     Specify from which ddGroup this DDView accepts drops. */
30141     setDroppable: function(ddGroup) {
30142         if (ddGroup instanceof Array) {
30143                 Roo.each(ddGroup, this.setDroppable, this);
30144                 return;
30145         }
30146         if (this.dropZone) {
30147                 this.dropZone.addToGroup(ddGroup);
30148         } else {
30149                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
30150                                 containerScroll: true,
30151                                 ddGroup: ddGroup
30152                         });
30153
30154 //                      Wire the DropZone's handlers up to methods in *this*
30155                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
30156                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
30157                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
30158                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
30159                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
30160                 }
30161     },
30162
30163 /**     Decide whether to drop above or below a View node. */
30164     getDropPoint : function(e, n, dd){
30165         if (n == this.el.dom) { return "above"; }
30166                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
30167                 var c = t + (b - t) / 2;
30168                 var y = Roo.lib.Event.getPageY(e);
30169                 if(y <= c) {
30170                         return "above";
30171                 }else{
30172                         return "below";
30173                 }
30174     },
30175
30176     onNodeEnter : function(n, dd, e, data){
30177                 return false;
30178     },
30179     
30180     onNodeOver : function(n, dd, e, data){
30181                 var pt = this.getDropPoint(e, n, dd);
30182                 // set the insert point style on the target node
30183                 var dragElClass = this.dropNotAllowed;
30184                 if (pt) {
30185                         var targetElClass;
30186                         if (pt == "above"){
30187                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
30188                                 targetElClass = "x-view-drag-insert-above";
30189                         } else {
30190                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
30191                                 targetElClass = "x-view-drag-insert-below";
30192                         }
30193                         if (this.lastInsertClass != targetElClass){
30194                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
30195                                 this.lastInsertClass = targetElClass;
30196                         }
30197                 }
30198                 return dragElClass;
30199         },
30200
30201     onNodeOut : function(n, dd, e, data){
30202                 this.removeDropIndicators(n);
30203     },
30204
30205     onNodeDrop : function(n, dd, e, data){
30206         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
30207                 return false;
30208         }
30209         var pt = this.getDropPoint(e, n, dd);
30210                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
30211                 if (pt == "below") { insertAt++; }
30212                 for (var i = 0; i < data.records.length; i++) {
30213                         var r = data.records[i];
30214                         var dup = this.store.getById(r.id);
30215                         if (dup && (dd != this.dragZone)) {
30216                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
30217                         } else {
30218                                 if (data.copy) {
30219                                         this.store.insert(insertAt++, r.copy());
30220                                 } else {
30221                                         data.source.isDirtyFlag = true;
30222                                         r.store.remove(r);
30223                                         this.store.insert(insertAt++, r);
30224                                 }
30225                                 this.isDirtyFlag = true;
30226                         }
30227                 }
30228                 this.dragZone.cachedTarget = null;
30229                 return true;
30230     },
30231
30232     removeDropIndicators : function(n){
30233                 if(n){
30234                         Roo.fly(n).removeClass([
30235                                 "x-view-drag-insert-above",
30236                                 "x-view-drag-insert-below"]);
30237                         this.lastInsertClass = "_noclass";
30238                 }
30239     },
30240
30241 /**
30242  *      Utility method. Add a delete option to the DDView's context menu.
30243  *      @param {String} imageUrl The URL of the "delete" icon image.
30244  */
30245         setDeletable: function(imageUrl) {
30246                 if (!this.singleSelect && !this.multiSelect) {
30247                         this.singleSelect = true;
30248                 }
30249                 var c = this.getContextMenu();
30250                 this.contextMenu.on("itemclick", function(item) {
30251                         switch (item.id) {
30252                                 case "delete":
30253                                         this.remove(this.getSelectedIndexes());
30254                                         break;
30255                         }
30256                 }, this);
30257                 this.contextMenu.add({
30258                         icon: imageUrl,
30259                         id: "delete",
30260                         text: 'Delete'
30261                 });
30262         },
30263         
30264 /**     Return the context menu for this DDView. */
30265         getContextMenu: function() {
30266                 if (!this.contextMenu) {
30267 //                      Create the View's context menu
30268                         this.contextMenu = new Roo.menu.Menu({
30269                                 id: this.id + "-contextmenu"
30270                         });
30271                         this.el.on("contextmenu", this.showContextMenu, this);
30272                 }
30273                 return this.contextMenu;
30274         },
30275         
30276         disableContextMenu: function() {
30277                 if (this.contextMenu) {
30278                         this.el.un("contextmenu", this.showContextMenu, this);
30279                 }
30280         },
30281
30282         showContextMenu: function(e, item) {
30283         item = this.findItemFromChild(e.getTarget());
30284                 if (item) {
30285                         e.stopEvent();
30286                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
30287                         this.contextMenu.showAt(e.getXY());
30288             }
30289     },
30290
30291 /**
30292  *      Remove {@link Roo.data.Record}s at the specified indices.
30293  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
30294  */
30295     remove: function(selectedIndices) {
30296                 selectedIndices = [].concat(selectedIndices);
30297                 for (var i = 0; i < selectedIndices.length; i++) {
30298                         var rec = this.store.getAt(selectedIndices[i]);
30299                         this.store.remove(rec);
30300                 }
30301     },
30302
30303 /**
30304  *      Double click fires the event, but also, if this is draggable, and there is only one other
30305  *      related DropZone, it transfers the selected node.
30306  */
30307     onDblClick : function(e){
30308         var item = this.findItemFromChild(e.getTarget());
30309         if(item){
30310             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
30311                 return false;
30312             }
30313             if (this.dragGroup) {
30314                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
30315                     while (targets.indexOf(this.dropZone) > -1) {
30316                             targets.remove(this.dropZone);
30317                                 }
30318                     if (targets.length == 1) {
30319                                         this.dragZone.cachedTarget = null;
30320                         var el = Roo.get(targets[0].getEl());
30321                         var box = el.getBox(true);
30322                         targets[0].onNodeDrop(el.dom, {
30323                                 target: el.dom,
30324                                 xy: [box.x, box.y + box.height - 1]
30325                         }, null, this.getDragData(e));
30326                     }
30327                 }
30328         }
30329     },
30330     
30331     handleSelection: function(e) {
30332                 this.dragZone.cachedTarget = null;
30333         var item = this.findItemFromChild(e.getTarget());
30334         if (!item) {
30335                 this.clearSelections(true);
30336                 return;
30337         }
30338                 if (item && (this.multiSelect || this.singleSelect)){
30339                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
30340                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
30341                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
30342                                 this.unselect(item);
30343                         } else {
30344                                 this.select(item, this.multiSelect && e.ctrlKey);
30345                                 this.lastSelection = item;
30346                         }
30347                 }
30348     },
30349
30350     onItemClick : function(item, index, e){
30351                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
30352                         return false;
30353                 }
30354                 return true;
30355     },
30356
30357     unselect : function(nodeInfo, suppressEvent){
30358                 var node = this.getNode(nodeInfo);
30359                 if(node && this.isSelected(node)){
30360                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
30361                                 Roo.fly(node).removeClass(this.selectedClass);
30362                                 this.selections.remove(node);
30363                                 if(!suppressEvent){
30364                                         this.fireEvent("selectionchange", this, this.selections);
30365                                 }
30366                         }
30367                 }
30368     }
30369 });
30370 /*
30371  * Based on:
30372  * Ext JS Library 1.1.1
30373  * Copyright(c) 2006-2007, Ext JS, LLC.
30374  *
30375  * Originally Released Under LGPL - original licence link has changed is not relivant.
30376  *
30377  * Fork - LGPL
30378  * <script type="text/javascript">
30379  */
30380  
30381 /**
30382  * @class Roo.LayoutManager
30383  * @extends Roo.util.Observable
30384  * Base class for layout managers.
30385  */
30386 Roo.LayoutManager = function(container, config){
30387     Roo.LayoutManager.superclass.constructor.call(this);
30388     this.el = Roo.get(container);
30389     // ie scrollbar fix
30390     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
30391         document.body.scroll = "no";
30392     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
30393         this.el.position('relative');
30394     }
30395     this.id = this.el.id;
30396     this.el.addClass("x-layout-container");
30397     /** false to disable window resize monitoring @type Boolean */
30398     this.monitorWindowResize = true;
30399     this.regions = {};
30400     this.addEvents({
30401         /**
30402          * @event layout
30403          * Fires when a layout is performed. 
30404          * @param {Roo.LayoutManager} this
30405          */
30406         "layout" : true,
30407         /**
30408          * @event regionresized
30409          * Fires when the user resizes a region. 
30410          * @param {Roo.LayoutRegion} region The resized region
30411          * @param {Number} newSize The new size (width for east/west, height for north/south)
30412          */
30413         "regionresized" : true,
30414         /**
30415          * @event regioncollapsed
30416          * Fires when a region is collapsed. 
30417          * @param {Roo.LayoutRegion} region The collapsed region
30418          */
30419         "regioncollapsed" : true,
30420         /**
30421          * @event regionexpanded
30422          * Fires when a region is expanded.  
30423          * @param {Roo.LayoutRegion} region The expanded region
30424          */
30425         "regionexpanded" : true
30426     });
30427     this.updating = false;
30428     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
30429 };
30430
30431 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
30432     /**
30433      * Returns true if this layout is currently being updated
30434      * @return {Boolean}
30435      */
30436     isUpdating : function(){
30437         return this.updating; 
30438     },
30439     
30440     /**
30441      * Suspend the LayoutManager from doing auto-layouts while
30442      * making multiple add or remove calls
30443      */
30444     beginUpdate : function(){
30445         this.updating = true;    
30446     },
30447     
30448     /**
30449      * Restore auto-layouts and optionally disable the manager from performing a layout
30450      * @param {Boolean} noLayout true to disable a layout update 
30451      */
30452     endUpdate : function(noLayout){
30453         this.updating = false;
30454         if(!noLayout){
30455             this.layout();
30456         }    
30457     },
30458     
30459     layout: function(){
30460         
30461     },
30462     
30463     onRegionResized : function(region, newSize){
30464         this.fireEvent("regionresized", region, newSize);
30465         this.layout();
30466     },
30467     
30468     onRegionCollapsed : function(region){
30469         this.fireEvent("regioncollapsed", region);
30470     },
30471     
30472     onRegionExpanded : function(region){
30473         this.fireEvent("regionexpanded", region);
30474     },
30475         
30476     /**
30477      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
30478      * performs box-model adjustments.
30479      * @return {Object} The size as an object {width: (the width), height: (the height)}
30480      */
30481     getViewSize : function(){
30482         var size;
30483         if(this.el.dom != document.body){
30484             size = this.el.getSize();
30485         }else{
30486             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
30487         }
30488         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
30489         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30490         return size;
30491     },
30492     
30493     /**
30494      * Returns the Element this layout is bound to.
30495      * @return {Roo.Element}
30496      */
30497     getEl : function(){
30498         return this.el;
30499     },
30500     
30501     /**
30502      * Returns the specified region.
30503      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
30504      * @return {Roo.LayoutRegion}
30505      */
30506     getRegion : function(target){
30507         return this.regions[target.toLowerCase()];
30508     },
30509     
30510     onWindowResize : function(){
30511         if(this.monitorWindowResize){
30512             this.layout();
30513         }
30514     }
30515 });/*
30516  * Based on:
30517  * Ext JS Library 1.1.1
30518  * Copyright(c) 2006-2007, Ext JS, LLC.
30519  *
30520  * Originally Released Under LGPL - original licence link has changed is not relivant.
30521  *
30522  * Fork - LGPL
30523  * <script type="text/javascript">
30524  */
30525 /**
30526  * @class Roo.BorderLayout
30527  * @extends Roo.LayoutManager
30528  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
30529  * please see: <br><br>
30530  * <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>
30531  * <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>
30532  * Example:
30533  <pre><code>
30534  var layout = new Roo.BorderLayout(document.body, {
30535     north: {
30536         initialSize: 25,
30537         titlebar: false
30538     },
30539     west: {
30540         split:true,
30541         initialSize: 200,
30542         minSize: 175,
30543         maxSize: 400,
30544         titlebar: true,
30545         collapsible: true
30546     },
30547     east: {
30548         split:true,
30549         initialSize: 202,
30550         minSize: 175,
30551         maxSize: 400,
30552         titlebar: true,
30553         collapsible: true
30554     },
30555     south: {
30556         split:true,
30557         initialSize: 100,
30558         minSize: 100,
30559         maxSize: 200,
30560         titlebar: true,
30561         collapsible: true
30562     },
30563     center: {
30564         titlebar: true,
30565         autoScroll:true,
30566         resizeTabs: true,
30567         minTabWidth: 50,
30568         preferredTabWidth: 150
30569     }
30570 });
30571
30572 // shorthand
30573 var CP = Roo.ContentPanel;
30574
30575 layout.beginUpdate();
30576 layout.add("north", new CP("north", "North"));
30577 layout.add("south", new CP("south", {title: "South", closable: true}));
30578 layout.add("west", new CP("west", {title: "West"}));
30579 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
30580 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
30581 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
30582 layout.getRegion("center").showPanel("center1");
30583 layout.endUpdate();
30584 </code></pre>
30585
30586 <b>The container the layout is rendered into can be either the body element or any other element.
30587 If it is not the body element, the container needs to either be an absolute positioned element,
30588 or you will need to add "position:relative" to the css of the container.  You will also need to specify
30589 the container size if it is not the body element.</b>
30590
30591 * @constructor
30592 * Create a new BorderLayout
30593 * @param {String/HTMLElement/Element} container The container this layout is bound to
30594 * @param {Object} config Configuration options
30595  */
30596 Roo.BorderLayout = function(container, config){
30597     config = config || {};
30598     Roo.BorderLayout.superclass.constructor.call(this, container, config);
30599     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
30600     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
30601         var target = this.factory.validRegions[i];
30602         if(config[target]){
30603             this.addRegion(target, config[target]);
30604         }
30605     }
30606 };
30607
30608 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
30609     /**
30610      * Creates and adds a new region if it doesn't already exist.
30611      * @param {String} target The target region key (north, south, east, west or center).
30612      * @param {Object} config The regions config object
30613      * @return {BorderLayoutRegion} The new region
30614      */
30615     addRegion : function(target, config){
30616         if(!this.regions[target]){
30617             var r = this.factory.create(target, this, config);
30618             this.bindRegion(target, r);
30619         }
30620         return this.regions[target];
30621     },
30622
30623     // private (kinda)
30624     bindRegion : function(name, r){
30625         this.regions[name] = r;
30626         r.on("visibilitychange", this.layout, this);
30627         r.on("paneladded", this.layout, this);
30628         r.on("panelremoved", this.layout, this);
30629         r.on("invalidated", this.layout, this);
30630         r.on("resized", this.onRegionResized, this);
30631         r.on("collapsed", this.onRegionCollapsed, this);
30632         r.on("expanded", this.onRegionExpanded, this);
30633     },
30634
30635     /**
30636      * Performs a layout update.
30637      */
30638     layout : function(){
30639         if(this.updating) return;
30640         var size = this.getViewSize();
30641         var w = size.width;
30642         var h = size.height;
30643         var centerW = w;
30644         var centerH = h;
30645         var centerY = 0;
30646         var centerX = 0;
30647         //var x = 0, y = 0;
30648
30649         var rs = this.regions;
30650         var north = rs["north"];
30651         var south = rs["south"]; 
30652         var west = rs["west"];
30653         var east = rs["east"];
30654         var center = rs["center"];
30655         //if(this.hideOnLayout){ // not supported anymore
30656             //c.el.setStyle("display", "none");
30657         //}
30658         if(north && north.isVisible()){
30659             var b = north.getBox();
30660             var m = north.getMargins();
30661             b.width = w - (m.left+m.right);
30662             b.x = m.left;
30663             b.y = m.top;
30664             centerY = b.height + b.y + m.bottom;
30665             centerH -= centerY;
30666             north.updateBox(this.safeBox(b));
30667         }
30668         if(south && south.isVisible()){
30669             var b = south.getBox();
30670             var m = south.getMargins();
30671             b.width = w - (m.left+m.right);
30672             b.x = m.left;
30673             var totalHeight = (b.height + m.top + m.bottom);
30674             b.y = h - totalHeight + m.top;
30675             centerH -= totalHeight;
30676             south.updateBox(this.safeBox(b));
30677         }
30678         if(west && west.isVisible()){
30679             var b = west.getBox();
30680             var m = west.getMargins();
30681             b.height = centerH - (m.top+m.bottom);
30682             b.x = m.left;
30683             b.y = centerY + m.top;
30684             var totalWidth = (b.width + m.left + m.right);
30685             centerX += totalWidth;
30686             centerW -= totalWidth;
30687             west.updateBox(this.safeBox(b));
30688         }
30689         if(east && east.isVisible()){
30690             var b = east.getBox();
30691             var m = east.getMargins();
30692             b.height = centerH - (m.top+m.bottom);
30693             var totalWidth = (b.width + m.left + m.right);
30694             b.x = w - totalWidth + m.left;
30695             b.y = centerY + m.top;
30696             centerW -= totalWidth;
30697             east.updateBox(this.safeBox(b));
30698         }
30699         if(center){
30700             var m = center.getMargins();
30701             var centerBox = {
30702                 x: centerX + m.left,
30703                 y: centerY + m.top,
30704                 width: centerW - (m.left+m.right),
30705                 height: centerH - (m.top+m.bottom)
30706             };
30707             //if(this.hideOnLayout){
30708                 //center.el.setStyle("display", "block");
30709             //}
30710             center.updateBox(this.safeBox(centerBox));
30711         }
30712         this.el.repaint();
30713         this.fireEvent("layout", this);
30714     },
30715
30716     // private
30717     safeBox : function(box){
30718         box.width = Math.max(0, box.width);
30719         box.height = Math.max(0, box.height);
30720         return box;
30721     },
30722
30723     /**
30724      * Adds a ContentPanel (or subclass) to this layout.
30725      * @param {String} target The target region key (north, south, east, west or center).
30726      * @param {Roo.ContentPanel} panel The panel to add
30727      * @return {Roo.ContentPanel} The added panel
30728      */
30729     add : function(target, panel){
30730          
30731         target = target.toLowerCase();
30732         return this.regions[target].add(panel);
30733     },
30734
30735     /**
30736      * Remove a ContentPanel (or subclass) to this layout.
30737      * @param {String} target The target region key (north, south, east, west or center).
30738      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
30739      * @return {Roo.ContentPanel} The removed panel
30740      */
30741     remove : function(target, panel){
30742         target = target.toLowerCase();
30743         return this.regions[target].remove(panel);
30744     },
30745
30746     /**
30747      * Searches all regions for a panel with the specified id
30748      * @param {String} panelId
30749      * @return {Roo.ContentPanel} The panel or null if it wasn't found
30750      */
30751     findPanel : function(panelId){
30752         var rs = this.regions;
30753         for(var target in rs){
30754             if(typeof rs[target] != "function"){
30755                 var p = rs[target].getPanel(panelId);
30756                 if(p){
30757                     return p;
30758                 }
30759             }
30760         }
30761         return null;
30762     },
30763
30764     /**
30765      * Searches all regions for a panel with the specified id and activates (shows) it.
30766      * @param {String/ContentPanel} panelId The panels id or the panel itself
30767      * @return {Roo.ContentPanel} The shown panel or null
30768      */
30769     showPanel : function(panelId) {
30770       var rs = this.regions;
30771       for(var target in rs){
30772          var r = rs[target];
30773          if(typeof r != "function"){
30774             if(r.hasPanel(panelId)){
30775                return r.showPanel(panelId);
30776             }
30777          }
30778       }
30779       return null;
30780    },
30781
30782    /**
30783      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
30784      * @param {Roo.state.Provider} provider (optional) An alternate state provider
30785      */
30786     restoreState : function(provider){
30787         if(!provider){
30788             provider = Roo.state.Manager;
30789         }
30790         var sm = new Roo.LayoutStateManager();
30791         sm.init(this, provider);
30792     },
30793
30794     /**
30795      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
30796      * object should contain properties for each region to add ContentPanels to, and each property's value should be
30797      * a valid ContentPanel config object.  Example:
30798      * <pre><code>
30799 // Create the main layout
30800 var layout = new Roo.BorderLayout('main-ct', {
30801     west: {
30802         split:true,
30803         minSize: 175,
30804         titlebar: true
30805     },
30806     center: {
30807         title:'Components'
30808     }
30809 }, 'main-ct');
30810
30811 // Create and add multiple ContentPanels at once via configs
30812 layout.batchAdd({
30813    west: {
30814        id: 'source-files',
30815        autoCreate:true,
30816        title:'Ext Source Files',
30817        autoScroll:true,
30818        fitToFrame:true
30819    },
30820    center : {
30821        el: cview,
30822        autoScroll:true,
30823        fitToFrame:true,
30824        toolbar: tb,
30825        resizeEl:'cbody'
30826    }
30827 });
30828 </code></pre>
30829      * @param {Object} regions An object containing ContentPanel configs by region name
30830      */
30831     batchAdd : function(regions){
30832         this.beginUpdate();
30833         for(var rname in regions){
30834             var lr = this.regions[rname];
30835             if(lr){
30836                 this.addTypedPanels(lr, regions[rname]);
30837             }
30838         }
30839         this.endUpdate();
30840     },
30841
30842     // private
30843     addTypedPanels : function(lr, ps){
30844         if(typeof ps == 'string'){
30845             lr.add(new Roo.ContentPanel(ps));
30846         }
30847         else if(ps instanceof Array){
30848             for(var i =0, len = ps.length; i < len; i++){
30849                 this.addTypedPanels(lr, ps[i]);
30850             }
30851         }
30852         else if(!ps.events){ // raw config?
30853             var el = ps.el;
30854             delete ps.el; // prevent conflict
30855             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
30856         }
30857         else {  // panel object assumed!
30858             lr.add(ps);
30859         }
30860     },
30861     /**
30862      * Adds a xtype elements to the layout.
30863      * <pre><code>
30864
30865 layout.addxtype({
30866        xtype : 'ContentPanel',
30867        region: 'west',
30868        items: [ .... ]
30869    }
30870 );
30871
30872 layout.addxtype({
30873         xtype : 'NestedLayoutPanel',
30874         region: 'west',
30875         layout: {
30876            center: { },
30877            west: { }   
30878         },
30879         items : [ ... list of content panels or nested layout panels.. ]
30880    }
30881 );
30882 </code></pre>
30883      * @param {Object} cfg Xtype definition of item to add.
30884      */
30885     addxtype : function(cfg)
30886     {
30887         // basically accepts a pannel...
30888         // can accept a layout region..!?!?
30889         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
30890         
30891         if (!cfg.xtype.match(/Panel$/)) {
30892             return false;
30893         }
30894         var ret = false;
30895         
30896         if (typeof(cfg.region) == 'undefined') {
30897             Roo.log("Failed to add Panel, region was not set");
30898             Roo.log(cfg);
30899             return false;
30900         }
30901         var region = cfg.region;
30902         delete cfg.region;
30903         
30904           
30905         var xitems = [];
30906         if (cfg.items) {
30907             xitems = cfg.items;
30908             delete cfg.items;
30909         }
30910         var nb = false;
30911         
30912         switch(cfg.xtype) 
30913         {
30914             case 'ContentPanel':  // ContentPanel (el, cfg)
30915             case 'ScrollPanel':  // ContentPanel (el, cfg)
30916                 if(cfg.autoCreate) {
30917                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30918                 } else {
30919                     var el = this.el.createChild();
30920                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
30921                 }
30922                 
30923                 this.add(region, ret);
30924                 break;
30925             
30926             
30927             case 'TreePanel': // our new panel!
30928                 cfg.el = this.el.createChild();
30929                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30930                 this.add(region, ret);
30931                 break;
30932             
30933             case 'NestedLayoutPanel': 
30934                 // create a new Layout (which is  a Border Layout...
30935                 var el = this.el.createChild();
30936                 var clayout = cfg.layout;
30937                 delete cfg.layout;
30938                 clayout.items   = clayout.items  || [];
30939                 // replace this exitems with the clayout ones..
30940                 xitems = clayout.items;
30941                  
30942                 
30943                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
30944                     cfg.background = false;
30945                 }
30946                 var layout = new Roo.BorderLayout(el, clayout);
30947                 
30948                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
30949                 //console.log('adding nested layout panel '  + cfg.toSource());
30950                 this.add(region, ret);
30951                 nb = {}; /// find first...
30952                 break;
30953                 
30954             case 'GridPanel': 
30955             
30956                 // needs grid and region
30957                 
30958                 //var el = this.getRegion(region).el.createChild();
30959                 var el = this.el.createChild();
30960                 // create the grid first...
30961                 
30962                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
30963                 delete cfg.grid;
30964                 if (region == 'center' && this.active ) {
30965                     cfg.background = false;
30966                 }
30967                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
30968                 
30969                 this.add(region, ret);
30970                 if (cfg.background) {
30971                     ret.on('activate', function(gp) {
30972                         if (!gp.grid.rendered) {
30973                             gp.grid.render();
30974                         }
30975                     });
30976                 } else {
30977                     grid.render();
30978                 }
30979                 break;
30980            
30981                
30982                 
30983                 
30984             default: 
30985                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
30986                 return null;
30987              // GridPanel (grid, cfg)
30988             
30989         }
30990         this.beginUpdate();
30991         // add children..
30992         var region = '';
30993         var abn = {};
30994         Roo.each(xitems, function(i)  {
30995             region = nb && i.region ? i.region : false;
30996             
30997             var add = ret.addxtype(i);
30998            
30999             if (region) {
31000                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31001                 if (!i.background) {
31002                     abn[region] = nb[region] ;
31003                 }
31004             }
31005             
31006         });
31007         this.endUpdate();
31008
31009         // make the last non-background panel active..
31010         //if (nb) { Roo.log(abn); }
31011         if (nb) {
31012             
31013             for(var r in abn) {
31014                 region = this.getRegion(r);
31015                 if (region) {
31016                     // tried using nb[r], but it does not work..
31017                      
31018                     region.showPanel(abn[r]);
31019                    
31020                 }
31021             }
31022         }
31023         return ret;
31024         
31025     }
31026 });
31027
31028 /**
31029  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
31030  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
31031  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
31032  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
31033  * <pre><code>
31034 // shorthand
31035 var CP = Roo.ContentPanel;
31036
31037 var layout = Roo.BorderLayout.create({
31038     north: {
31039         initialSize: 25,
31040         titlebar: false,
31041         panels: [new CP("north", "North")]
31042     },
31043     west: {
31044         split:true,
31045         initialSize: 200,
31046         minSize: 175,
31047         maxSize: 400,
31048         titlebar: true,
31049         collapsible: true,
31050         panels: [new CP("west", {title: "West"})]
31051     },
31052     east: {
31053         split:true,
31054         initialSize: 202,
31055         minSize: 175,
31056         maxSize: 400,
31057         titlebar: true,
31058         collapsible: true,
31059         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
31060     },
31061     south: {
31062         split:true,
31063         initialSize: 100,
31064         minSize: 100,
31065         maxSize: 200,
31066         titlebar: true,
31067         collapsible: true,
31068         panels: [new CP("south", {title: "South", closable: true})]
31069     },
31070     center: {
31071         titlebar: true,
31072         autoScroll:true,
31073         resizeTabs: true,
31074         minTabWidth: 50,
31075         preferredTabWidth: 150,
31076         panels: [
31077             new CP("center1", {title: "Close Me", closable: true}),
31078             new CP("center2", {title: "Center Panel", closable: false})
31079         ]
31080     }
31081 }, document.body);
31082
31083 layout.getRegion("center").showPanel("center1");
31084 </code></pre>
31085  * @param config
31086  * @param targetEl
31087  */
31088 Roo.BorderLayout.create = function(config, targetEl){
31089     var layout = new Roo.BorderLayout(targetEl || document.body, config);
31090     layout.beginUpdate();
31091     var regions = Roo.BorderLayout.RegionFactory.validRegions;
31092     for(var j = 0, jlen = regions.length; j < jlen; j++){
31093         var lr = regions[j];
31094         if(layout.regions[lr] && config[lr].panels){
31095             var r = layout.regions[lr];
31096             var ps = config[lr].panels;
31097             layout.addTypedPanels(r, ps);
31098         }
31099     }
31100     layout.endUpdate();
31101     return layout;
31102 };
31103
31104 // private
31105 Roo.BorderLayout.RegionFactory = {
31106     // private
31107     validRegions : ["north","south","east","west","center"],
31108
31109     // private
31110     create : function(target, mgr, config){
31111         target = target.toLowerCase();
31112         if(config.lightweight || config.basic){
31113             return new Roo.BasicLayoutRegion(mgr, config, target);
31114         }
31115         switch(target){
31116             case "north":
31117                 return new Roo.NorthLayoutRegion(mgr, config);
31118             case "south":
31119                 return new Roo.SouthLayoutRegion(mgr, config);
31120             case "east":
31121                 return new Roo.EastLayoutRegion(mgr, config);
31122             case "west":
31123                 return new Roo.WestLayoutRegion(mgr, config);
31124             case "center":
31125                 return new Roo.CenterLayoutRegion(mgr, config);
31126         }
31127         throw 'Layout region "'+target+'" not supported.';
31128     }
31129 };/*
31130  * Based on:
31131  * Ext JS Library 1.1.1
31132  * Copyright(c) 2006-2007, Ext JS, LLC.
31133  *
31134  * Originally Released Under LGPL - original licence link has changed is not relivant.
31135  *
31136  * Fork - LGPL
31137  * <script type="text/javascript">
31138  */
31139  
31140 /**
31141  * @class Roo.BasicLayoutRegion
31142  * @extends Roo.util.Observable
31143  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31144  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31145  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31146  */
31147 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
31148     this.mgr = mgr;
31149     this.position  = pos;
31150     this.events = {
31151         /**
31152          * @scope Roo.BasicLayoutRegion
31153          */
31154         
31155         /**
31156          * @event beforeremove
31157          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31158          * @param {Roo.LayoutRegion} this
31159          * @param {Roo.ContentPanel} panel The panel
31160          * @param {Object} e The cancel event object
31161          */
31162         "beforeremove" : true,
31163         /**
31164          * @event invalidated
31165          * Fires when the layout for this region is changed.
31166          * @param {Roo.LayoutRegion} this
31167          */
31168         "invalidated" : true,
31169         /**
31170          * @event visibilitychange
31171          * Fires when this region is shown or hidden 
31172          * @param {Roo.LayoutRegion} this
31173          * @param {Boolean} visibility true or false
31174          */
31175         "visibilitychange" : true,
31176         /**
31177          * @event paneladded
31178          * Fires when a panel is added. 
31179          * @param {Roo.LayoutRegion} this
31180          * @param {Roo.ContentPanel} panel The panel
31181          */
31182         "paneladded" : true,
31183         /**
31184          * @event panelremoved
31185          * Fires when a panel is removed. 
31186          * @param {Roo.LayoutRegion} this
31187          * @param {Roo.ContentPanel} panel The panel
31188          */
31189         "panelremoved" : true,
31190         /**
31191          * @event collapsed
31192          * Fires when this region is collapsed.
31193          * @param {Roo.LayoutRegion} this
31194          */
31195         "collapsed" : true,
31196         /**
31197          * @event expanded
31198          * Fires when this region is expanded.
31199          * @param {Roo.LayoutRegion} this
31200          */
31201         "expanded" : true,
31202         /**
31203          * @event slideshow
31204          * Fires when this region is slid into view.
31205          * @param {Roo.LayoutRegion} this
31206          */
31207         "slideshow" : true,
31208         /**
31209          * @event slidehide
31210          * Fires when this region slides out of view. 
31211          * @param {Roo.LayoutRegion} this
31212          */
31213         "slidehide" : true,
31214         /**
31215          * @event panelactivated
31216          * Fires when a panel is activated. 
31217          * @param {Roo.LayoutRegion} this
31218          * @param {Roo.ContentPanel} panel The activated panel
31219          */
31220         "panelactivated" : true,
31221         /**
31222          * @event resized
31223          * Fires when the user resizes this region. 
31224          * @param {Roo.LayoutRegion} this
31225          * @param {Number} newSize The new size (width for east/west, height for north/south)
31226          */
31227         "resized" : true
31228     };
31229     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31230     this.panels = new Roo.util.MixedCollection();
31231     this.panels.getKey = this.getPanelId.createDelegate(this);
31232     this.box = null;
31233     this.activePanel = null;
31234     // ensure listeners are added...
31235     
31236     if (config.listeners || config.events) {
31237         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
31238             listeners : config.listeners || {},
31239             events : config.events || {}
31240         });
31241     }
31242     
31243     if(skipConfig !== true){
31244         this.applyConfig(config);
31245     }
31246 };
31247
31248 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
31249     getPanelId : function(p){
31250         return p.getId();
31251     },
31252     
31253     applyConfig : function(config){
31254         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31255         this.config = config;
31256         
31257     },
31258     
31259     /**
31260      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31261      * the width, for horizontal (north, south) the height.
31262      * @param {Number} newSize The new width or height
31263      */
31264     resizeTo : function(newSize){
31265         var el = this.el ? this.el :
31266                  (this.activePanel ? this.activePanel.getEl() : null);
31267         if(el){
31268             switch(this.position){
31269                 case "east":
31270                 case "west":
31271                     el.setWidth(newSize);
31272                     this.fireEvent("resized", this, newSize);
31273                 break;
31274                 case "north":
31275                 case "south":
31276                     el.setHeight(newSize);
31277                     this.fireEvent("resized", this, newSize);
31278                 break;                
31279             }
31280         }
31281     },
31282     
31283     getBox : function(){
31284         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31285     },
31286     
31287     getMargins : function(){
31288         return this.margins;
31289     },
31290     
31291     updateBox : function(box){
31292         this.box = box;
31293         var el = this.activePanel.getEl();
31294         el.dom.style.left = box.x + "px";
31295         el.dom.style.top = box.y + "px";
31296         this.activePanel.setSize(box.width, box.height);
31297     },
31298     
31299     /**
31300      * Returns the container element for this region.
31301      * @return {Roo.Element}
31302      */
31303     getEl : function(){
31304         return this.activePanel;
31305     },
31306     
31307     /**
31308      * Returns true if this region is currently visible.
31309      * @return {Boolean}
31310      */
31311     isVisible : function(){
31312         return this.activePanel ? true : false;
31313     },
31314     
31315     setActivePanel : function(panel){
31316         panel = this.getPanel(panel);
31317         if(this.activePanel && this.activePanel != panel){
31318             this.activePanel.setActiveState(false);
31319             this.activePanel.getEl().setLeftTop(-10000,-10000);
31320         }
31321         this.activePanel = panel;
31322         panel.setActiveState(true);
31323         if(this.box){
31324             panel.setSize(this.box.width, this.box.height);
31325         }
31326         this.fireEvent("panelactivated", this, panel);
31327         this.fireEvent("invalidated");
31328     },
31329     
31330     /**
31331      * Show the specified panel.
31332      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31333      * @return {Roo.ContentPanel} The shown panel or null
31334      */
31335     showPanel : function(panel){
31336         if(panel = this.getPanel(panel)){
31337             this.setActivePanel(panel);
31338         }
31339         return panel;
31340     },
31341     
31342     /**
31343      * Get the active panel for this region.
31344      * @return {Roo.ContentPanel} The active panel or null
31345      */
31346     getActivePanel : function(){
31347         return this.activePanel;
31348     },
31349     
31350     /**
31351      * Add the passed ContentPanel(s)
31352      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31353      * @return {Roo.ContentPanel} The panel added (if only one was added)
31354      */
31355     add : function(panel){
31356         if(arguments.length > 1){
31357             for(var i = 0, len = arguments.length; i < len; i++) {
31358                 this.add(arguments[i]);
31359             }
31360             return null;
31361         }
31362         if(this.hasPanel(panel)){
31363             this.showPanel(panel);
31364             return panel;
31365         }
31366         var el = panel.getEl();
31367         if(el.dom.parentNode != this.mgr.el.dom){
31368             this.mgr.el.dom.appendChild(el.dom);
31369         }
31370         if(panel.setRegion){
31371             panel.setRegion(this);
31372         }
31373         this.panels.add(panel);
31374         el.setStyle("position", "absolute");
31375         if(!panel.background){
31376             this.setActivePanel(panel);
31377             if(this.config.initialSize && this.panels.getCount()==1){
31378                 this.resizeTo(this.config.initialSize);
31379             }
31380         }
31381         this.fireEvent("paneladded", this, panel);
31382         return panel;
31383     },
31384     
31385     /**
31386      * Returns true if the panel is in this region.
31387      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31388      * @return {Boolean}
31389      */
31390     hasPanel : function(panel){
31391         if(typeof panel == "object"){ // must be panel obj
31392             panel = panel.getId();
31393         }
31394         return this.getPanel(panel) ? true : false;
31395     },
31396     
31397     /**
31398      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31399      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31400      * @param {Boolean} preservePanel Overrides the config preservePanel option
31401      * @return {Roo.ContentPanel} The panel that was removed
31402      */
31403     remove : function(panel, preservePanel){
31404         panel = this.getPanel(panel);
31405         if(!panel){
31406             return null;
31407         }
31408         var e = {};
31409         this.fireEvent("beforeremove", this, panel, e);
31410         if(e.cancel === true){
31411             return null;
31412         }
31413         var panelId = panel.getId();
31414         this.panels.removeKey(panelId);
31415         return panel;
31416     },
31417     
31418     /**
31419      * Returns the panel specified or null if it's not in this region.
31420      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31421      * @return {Roo.ContentPanel}
31422      */
31423     getPanel : function(id){
31424         if(typeof id == "object"){ // must be panel obj
31425             return id;
31426         }
31427         return this.panels.get(id);
31428     },
31429     
31430     /**
31431      * Returns this regions position (north/south/east/west/center).
31432      * @return {String} 
31433      */
31434     getPosition: function(){
31435         return this.position;    
31436     }
31437 });/*
31438  * Based on:
31439  * Ext JS Library 1.1.1
31440  * Copyright(c) 2006-2007, Ext JS, LLC.
31441  *
31442  * Originally Released Under LGPL - original licence link has changed is not relivant.
31443  *
31444  * Fork - LGPL
31445  * <script type="text/javascript">
31446  */
31447  
31448 /**
31449  * @class Roo.LayoutRegion
31450  * @extends Roo.BasicLayoutRegion
31451  * This class represents a region in a layout manager.
31452  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
31453  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
31454  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
31455  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
31456  * @cfg {Object}    cmargins        Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
31457  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
31458  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
31459  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
31460  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
31461  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
31462  * @cfg {String}    title           The title for the region (overrides panel titles)
31463  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
31464  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
31465  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
31466  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
31467  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
31468  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
31469  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
31470  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
31471  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
31472  * @cfg {Boolean}   showPin         True to show a pin button
31473  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
31474  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
31475  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
31476  * @cfg {Number}    width           For East/West panels
31477  * @cfg {Number}    height          For North/South panels
31478  * @cfg {Boolean}   split           To show the splitter
31479  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
31480  */
31481 Roo.LayoutRegion = function(mgr, config, pos){
31482     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
31483     var dh = Roo.DomHelper;
31484     /** This region's container element 
31485     * @type Roo.Element */
31486     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
31487     /** This region's title element 
31488     * @type Roo.Element */
31489
31490     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
31491         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
31492         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
31493     ]}, true);
31494     this.titleEl.enableDisplayMode();
31495     /** This region's title text element 
31496     * @type HTMLElement */
31497     this.titleTextEl = this.titleEl.dom.firstChild;
31498     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
31499     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
31500     this.closeBtn.enableDisplayMode();
31501     this.closeBtn.on("click", this.closeClicked, this);
31502     this.closeBtn.hide();
31503
31504     this.createBody(config);
31505     this.visible = true;
31506     this.collapsed = false;
31507
31508     if(config.hideWhenEmpty){
31509         this.hide();
31510         this.on("paneladded", this.validateVisibility, this);
31511         this.on("panelremoved", this.validateVisibility, this);
31512     }
31513     this.applyConfig(config);
31514 };
31515
31516 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
31517
31518     createBody : function(){
31519         /** This region's body element 
31520         * @type Roo.Element */
31521         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
31522     },
31523
31524     applyConfig : function(c){
31525         if(c.collapsible && this.position != "center" && !this.collapsedEl){
31526             var dh = Roo.DomHelper;
31527             if(c.titlebar !== false){
31528                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
31529                 this.collapseBtn.on("click", this.collapse, this);
31530                 this.collapseBtn.enableDisplayMode();
31531
31532                 if(c.showPin === true || this.showPin){
31533                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
31534                     this.stickBtn.enableDisplayMode();
31535                     this.stickBtn.on("click", this.expand, this);
31536                     this.stickBtn.hide();
31537                 }
31538             }
31539             /** This region's collapsed element
31540             * @type Roo.Element */
31541             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
31542                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
31543             ]}, true);
31544             if(c.floatable !== false){
31545                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
31546                this.collapsedEl.on("click", this.collapseClick, this);
31547             }
31548
31549             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
31550                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
31551                    id: "message", unselectable: "on", style:{"float":"left"}});
31552                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
31553              }
31554             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
31555             this.expandBtn.on("click", this.expand, this);
31556         }
31557         if(this.collapseBtn){
31558             this.collapseBtn.setVisible(c.collapsible == true);
31559         }
31560         this.cmargins = c.cmargins || this.cmargins ||
31561                          (this.position == "west" || this.position == "east" ?
31562                              {top: 0, left: 2, right:2, bottom: 0} :
31563                              {top: 2, left: 0, right:0, bottom: 2});
31564         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31565         this.bottomTabs = c.tabPosition != "top";
31566         this.autoScroll = c.autoScroll || false;
31567         if(this.autoScroll){
31568             this.bodyEl.setStyle("overflow", "auto");
31569         }else{
31570             this.bodyEl.setStyle("overflow", "hidden");
31571         }
31572         //if(c.titlebar !== false){
31573             if((!c.titlebar && !c.title) || c.titlebar === false){
31574                 this.titleEl.hide();
31575             }else{
31576                 this.titleEl.show();
31577                 if(c.title){
31578                     this.titleTextEl.innerHTML = c.title;
31579                 }
31580             }
31581         //}
31582         this.duration = c.duration || .30;
31583         this.slideDuration = c.slideDuration || .45;
31584         this.config = c;
31585         if(c.collapsed){
31586             this.collapse(true);
31587         }
31588         if(c.hidden){
31589             this.hide();
31590         }
31591     },
31592     /**
31593      * Returns true if this region is currently visible.
31594      * @return {Boolean}
31595      */
31596     isVisible : function(){
31597         return this.visible;
31598     },
31599
31600     /**
31601      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
31602      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
31603      */
31604     setCollapsedTitle : function(title){
31605         title = title || "&#160;";
31606         if(this.collapsedTitleTextEl){
31607             this.collapsedTitleTextEl.innerHTML = title;
31608         }
31609     },
31610
31611     getBox : function(){
31612         var b;
31613         if(!this.collapsed){
31614             b = this.el.getBox(false, true);
31615         }else{
31616             b = this.collapsedEl.getBox(false, true);
31617         }
31618         return b;
31619     },
31620
31621     getMargins : function(){
31622         return this.collapsed ? this.cmargins : this.margins;
31623     },
31624
31625     highlight : function(){
31626         this.el.addClass("x-layout-panel-dragover");
31627     },
31628
31629     unhighlight : function(){
31630         this.el.removeClass("x-layout-panel-dragover");
31631     },
31632
31633     updateBox : function(box){
31634         this.box = box;
31635         if(!this.collapsed){
31636             this.el.dom.style.left = box.x + "px";
31637             this.el.dom.style.top = box.y + "px";
31638             this.updateBody(box.width, box.height);
31639         }else{
31640             this.collapsedEl.dom.style.left = box.x + "px";
31641             this.collapsedEl.dom.style.top = box.y + "px";
31642             this.collapsedEl.setSize(box.width, box.height);
31643         }
31644         if(this.tabs){
31645             this.tabs.autoSizeTabs();
31646         }
31647     },
31648
31649     updateBody : function(w, h){
31650         if(w !== null){
31651             this.el.setWidth(w);
31652             w -= this.el.getBorderWidth("rl");
31653             if(this.config.adjustments){
31654                 w += this.config.adjustments[0];
31655             }
31656         }
31657         if(h !== null){
31658             this.el.setHeight(h);
31659             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
31660             h -= this.el.getBorderWidth("tb");
31661             if(this.config.adjustments){
31662                 h += this.config.adjustments[1];
31663             }
31664             this.bodyEl.setHeight(h);
31665             if(this.tabs){
31666                 h = this.tabs.syncHeight(h);
31667             }
31668         }
31669         if(this.panelSize){
31670             w = w !== null ? w : this.panelSize.width;
31671             h = h !== null ? h : this.panelSize.height;
31672         }
31673         if(this.activePanel){
31674             var el = this.activePanel.getEl();
31675             w = w !== null ? w : el.getWidth();
31676             h = h !== null ? h : el.getHeight();
31677             this.panelSize = {width: w, height: h};
31678             this.activePanel.setSize(w, h);
31679         }
31680         if(Roo.isIE && this.tabs){
31681             this.tabs.el.repaint();
31682         }
31683     },
31684
31685     /**
31686      * Returns the container element for this region.
31687      * @return {Roo.Element}
31688      */
31689     getEl : function(){
31690         return this.el;
31691     },
31692
31693     /**
31694      * Hides this region.
31695      */
31696     hide : function(){
31697         if(!this.collapsed){
31698             this.el.dom.style.left = "-2000px";
31699             this.el.hide();
31700         }else{
31701             this.collapsedEl.dom.style.left = "-2000px";
31702             this.collapsedEl.hide();
31703         }
31704         this.visible = false;
31705         this.fireEvent("visibilitychange", this, false);
31706     },
31707
31708     /**
31709      * Shows this region if it was previously hidden.
31710      */
31711     show : function(){
31712         if(!this.collapsed){
31713             this.el.show();
31714         }else{
31715             this.collapsedEl.show();
31716         }
31717         this.visible = true;
31718         this.fireEvent("visibilitychange", this, true);
31719     },
31720
31721     closeClicked : function(){
31722         if(this.activePanel){
31723             this.remove(this.activePanel);
31724         }
31725     },
31726
31727     collapseClick : function(e){
31728         if(this.isSlid){
31729            e.stopPropagation();
31730            this.slideIn();
31731         }else{
31732            e.stopPropagation();
31733            this.slideOut();
31734         }
31735     },
31736
31737     /**
31738      * Collapses this region.
31739      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
31740      */
31741     collapse : function(skipAnim){
31742         if(this.collapsed) return;
31743         this.collapsed = true;
31744         if(this.split){
31745             this.split.el.hide();
31746         }
31747         if(this.config.animate && skipAnim !== true){
31748             this.fireEvent("invalidated", this);
31749             this.animateCollapse();
31750         }else{
31751             this.el.setLocation(-20000,-20000);
31752             this.el.hide();
31753             this.collapsedEl.show();
31754             this.fireEvent("collapsed", this);
31755             this.fireEvent("invalidated", this);
31756         }
31757     },
31758
31759     animateCollapse : function(){
31760         // overridden
31761     },
31762
31763     /**
31764      * Expands this region if it was previously collapsed.
31765      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
31766      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
31767      */
31768     expand : function(e, skipAnim){
31769         if(e) e.stopPropagation();
31770         if(!this.collapsed || this.el.hasActiveFx()) return;
31771         if(this.isSlid){
31772             this.afterSlideIn();
31773             skipAnim = true;
31774         }
31775         this.collapsed = false;
31776         if(this.config.animate && skipAnim !== true){
31777             this.animateExpand();
31778         }else{
31779             this.el.show();
31780             if(this.split){
31781                 this.split.el.show();
31782             }
31783             this.collapsedEl.setLocation(-2000,-2000);
31784             this.collapsedEl.hide();
31785             this.fireEvent("invalidated", this);
31786             this.fireEvent("expanded", this);
31787         }
31788     },
31789
31790     animateExpand : function(){
31791         // overridden
31792     },
31793
31794     initTabs : function()
31795     {
31796         this.bodyEl.setStyle("overflow", "hidden");
31797         var ts = new Roo.TabPanel(
31798                 this.bodyEl.dom,
31799                 {
31800                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
31801                     disableTooltips: this.config.disableTabTips,
31802                     toolbar : this.config.toolbar
31803                 }
31804         );
31805         if(this.config.hideTabs){
31806             ts.stripWrap.setDisplayed(false);
31807         }
31808         this.tabs = ts;
31809         ts.resizeTabs = this.config.resizeTabs === true;
31810         ts.minTabWidth = this.config.minTabWidth || 40;
31811         ts.maxTabWidth = this.config.maxTabWidth || 250;
31812         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
31813         ts.monitorResize = false;
31814         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31815         ts.bodyEl.addClass('x-layout-tabs-body');
31816         this.panels.each(this.initPanelAsTab, this);
31817     },
31818
31819     initPanelAsTab : function(panel){
31820         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
31821                     this.config.closeOnTab && panel.isClosable());
31822         if(panel.tabTip !== undefined){
31823             ti.setTooltip(panel.tabTip);
31824         }
31825         ti.on("activate", function(){
31826               this.setActivePanel(panel);
31827         }, this);
31828         if(this.config.closeOnTab){
31829             ti.on("beforeclose", function(t, e){
31830                 e.cancel = true;
31831                 this.remove(panel);
31832             }, this);
31833         }
31834         return ti;
31835     },
31836
31837     updatePanelTitle : function(panel, title){
31838         if(this.activePanel == panel){
31839             this.updateTitle(title);
31840         }
31841         if(this.tabs){
31842             var ti = this.tabs.getTab(panel.getEl().id);
31843             ti.setText(title);
31844             if(panel.tabTip !== undefined){
31845                 ti.setTooltip(panel.tabTip);
31846             }
31847         }
31848     },
31849
31850     updateTitle : function(title){
31851         if(this.titleTextEl && !this.config.title){
31852             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
31853         }
31854     },
31855
31856     setActivePanel : function(panel){
31857         panel = this.getPanel(panel);
31858         if(this.activePanel && this.activePanel != panel){
31859             this.activePanel.setActiveState(false);
31860         }
31861         this.activePanel = panel;
31862         panel.setActiveState(true);
31863         if(this.panelSize){
31864             panel.setSize(this.panelSize.width, this.panelSize.height);
31865         }
31866         if(this.closeBtn){
31867             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
31868         }
31869         this.updateTitle(panel.getTitle());
31870         if(this.tabs){
31871             this.fireEvent("invalidated", this);
31872         }
31873         this.fireEvent("panelactivated", this, panel);
31874     },
31875
31876     /**
31877      * Shows the specified panel.
31878      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
31879      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
31880      */
31881     showPanel : function(panel){
31882         if(panel = this.getPanel(panel)){
31883             if(this.tabs){
31884                 var tab = this.tabs.getTab(panel.getEl().id);
31885                 if(tab.isHidden()){
31886                     this.tabs.unhideTab(tab.id);
31887                 }
31888                 tab.activate();
31889             }else{
31890                 this.setActivePanel(panel);
31891             }
31892         }
31893         return panel;
31894     },
31895
31896     /**
31897      * Get the active panel for this region.
31898      * @return {Roo.ContentPanel} The active panel or null
31899      */
31900     getActivePanel : function(){
31901         return this.activePanel;
31902     },
31903
31904     validateVisibility : function(){
31905         if(this.panels.getCount() < 1){
31906             this.updateTitle("&#160;");
31907             this.closeBtn.hide();
31908             this.hide();
31909         }else{
31910             if(!this.isVisible()){
31911                 this.show();
31912             }
31913         }
31914     },
31915
31916     /**
31917      * Adds the passed ContentPanel(s) to this region.
31918      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31919      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
31920      */
31921     add : function(panel){
31922         if(arguments.length > 1){
31923             for(var i = 0, len = arguments.length; i < len; i++) {
31924                 this.add(arguments[i]);
31925             }
31926             return null;
31927         }
31928         if(this.hasPanel(panel)){
31929             this.showPanel(panel);
31930             return panel;
31931         }
31932         panel.setRegion(this);
31933         this.panels.add(panel);
31934         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
31935             this.bodyEl.dom.appendChild(panel.getEl().dom);
31936             if(panel.background !== true){
31937                 this.setActivePanel(panel);
31938             }
31939             this.fireEvent("paneladded", this, panel);
31940             return panel;
31941         }
31942         if(!this.tabs){
31943             this.initTabs();
31944         }else{
31945             this.initPanelAsTab(panel);
31946         }
31947         if(panel.background !== true){
31948             this.tabs.activate(panel.getEl().id);
31949         }
31950         this.fireEvent("paneladded", this, panel);
31951         return panel;
31952     },
31953
31954     /**
31955      * Hides the tab for the specified panel.
31956      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31957      */
31958     hidePanel : function(panel){
31959         if(this.tabs && (panel = this.getPanel(panel))){
31960             this.tabs.hideTab(panel.getEl().id);
31961         }
31962     },
31963
31964     /**
31965      * Unhides the tab for a previously hidden panel.
31966      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31967      */
31968     unhidePanel : function(panel){
31969         if(this.tabs && (panel = this.getPanel(panel))){
31970             this.tabs.unhideTab(panel.getEl().id);
31971         }
31972     },
31973
31974     clearPanels : function(){
31975         while(this.panels.getCount() > 0){
31976              this.remove(this.panels.first());
31977         }
31978     },
31979
31980     /**
31981      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31982      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31983      * @param {Boolean} preservePanel Overrides the config preservePanel option
31984      * @return {Roo.ContentPanel} The panel that was removed
31985      */
31986     remove : function(panel, preservePanel){
31987         panel = this.getPanel(panel);
31988         if(!panel){
31989             return null;
31990         }
31991         var e = {};
31992         this.fireEvent("beforeremove", this, panel, e);
31993         if(e.cancel === true){
31994             return null;
31995         }
31996         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
31997         var panelId = panel.getId();
31998         this.panels.removeKey(panelId);
31999         if(preservePanel){
32000             document.body.appendChild(panel.getEl().dom);
32001         }
32002         if(this.tabs){
32003             this.tabs.removeTab(panel.getEl().id);
32004         }else if (!preservePanel){
32005             this.bodyEl.dom.removeChild(panel.getEl().dom);
32006         }
32007         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32008             var p = this.panels.first();
32009             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32010             tempEl.appendChild(p.getEl().dom);
32011             this.bodyEl.update("");
32012             this.bodyEl.dom.appendChild(p.getEl().dom);
32013             tempEl = null;
32014             this.updateTitle(p.getTitle());
32015             this.tabs = null;
32016             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32017             this.setActivePanel(p);
32018         }
32019         panel.setRegion(null);
32020         if(this.activePanel == panel){
32021             this.activePanel = null;
32022         }
32023         if(this.config.autoDestroy !== false && preservePanel !== true){
32024             try{panel.destroy();}catch(e){}
32025         }
32026         this.fireEvent("panelremoved", this, panel);
32027         return panel;
32028     },
32029
32030     /**
32031      * Returns the TabPanel component used by this region
32032      * @return {Roo.TabPanel}
32033      */
32034     getTabs : function(){
32035         return this.tabs;
32036     },
32037
32038     createTool : function(parentEl, className){
32039         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
32040             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
32041         btn.addClassOnOver("x-layout-tools-button-over");
32042         return btn;
32043     }
32044 });/*
32045  * Based on:
32046  * Ext JS Library 1.1.1
32047  * Copyright(c) 2006-2007, Ext JS, LLC.
32048  *
32049  * Originally Released Under LGPL - original licence link has changed is not relivant.
32050  *
32051  * Fork - LGPL
32052  * <script type="text/javascript">
32053  */
32054  
32055
32056
32057 /**
32058  * @class Roo.SplitLayoutRegion
32059  * @extends Roo.LayoutRegion
32060  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32061  */
32062 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
32063     this.cursor = cursor;
32064     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
32065 };
32066
32067 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
32068     splitTip : "Drag to resize.",
32069     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32070     useSplitTips : false,
32071
32072     applyConfig : function(config){
32073         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
32074         if(config.split){
32075             if(!this.split){
32076                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
32077                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
32078                 /** The SplitBar for this region 
32079                 * @type Roo.SplitBar */
32080                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
32081                 this.split.on("moved", this.onSplitMove, this);
32082                 this.split.useShim = config.useShim === true;
32083                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32084                 if(this.useSplitTips){
32085                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32086                 }
32087                 if(config.collapsible){
32088                     this.split.el.on("dblclick", this.collapse,  this);
32089                 }
32090             }
32091             if(typeof config.minSize != "undefined"){
32092                 this.split.minSize = config.minSize;
32093             }
32094             if(typeof config.maxSize != "undefined"){
32095                 this.split.maxSize = config.maxSize;
32096             }
32097             if(config.hideWhenEmpty || config.hidden || config.collapsed){
32098                 this.hideSplitter();
32099             }
32100         }
32101     },
32102
32103     getHMaxSize : function(){
32104          var cmax = this.config.maxSize || 10000;
32105          var center = this.mgr.getRegion("center");
32106          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32107     },
32108
32109     getVMaxSize : function(){
32110          var cmax = this.config.maxSize || 10000;
32111          var center = this.mgr.getRegion("center");
32112          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32113     },
32114
32115     onSplitMove : function(split, newSize){
32116         this.fireEvent("resized", this, newSize);
32117     },
32118     
32119     /** 
32120      * Returns the {@link Roo.SplitBar} for this region.
32121      * @return {Roo.SplitBar}
32122      */
32123     getSplitBar : function(){
32124         return this.split;
32125     },
32126     
32127     hide : function(){
32128         this.hideSplitter();
32129         Roo.SplitLayoutRegion.superclass.hide.call(this);
32130     },
32131
32132     hideSplitter : function(){
32133         if(this.split){
32134             this.split.el.setLocation(-2000,-2000);
32135             this.split.el.hide();
32136         }
32137     },
32138
32139     show : function(){
32140         if(this.split){
32141             this.split.el.show();
32142         }
32143         Roo.SplitLayoutRegion.superclass.show.call(this);
32144     },
32145     
32146     beforeSlide: function(){
32147         if(Roo.isGecko){// firefox overflow auto bug workaround
32148             this.bodyEl.clip();
32149             if(this.tabs) this.tabs.bodyEl.clip();
32150             if(this.activePanel){
32151                 this.activePanel.getEl().clip();
32152                 
32153                 if(this.activePanel.beforeSlide){
32154                     this.activePanel.beforeSlide();
32155                 }
32156             }
32157         }
32158     },
32159     
32160     afterSlide : function(){
32161         if(Roo.isGecko){// firefox overflow auto bug workaround
32162             this.bodyEl.unclip();
32163             if(this.tabs) this.tabs.bodyEl.unclip();
32164             if(this.activePanel){
32165                 this.activePanel.getEl().unclip();
32166                 if(this.activePanel.afterSlide){
32167                     this.activePanel.afterSlide();
32168                 }
32169             }
32170         }
32171     },
32172
32173     initAutoHide : function(){
32174         if(this.autoHide !== false){
32175             if(!this.autoHideHd){
32176                 var st = new Roo.util.DelayedTask(this.slideIn, this);
32177                 this.autoHideHd = {
32178                     "mouseout": function(e){
32179                         if(!e.within(this.el, true)){
32180                             st.delay(500);
32181                         }
32182                     },
32183                     "mouseover" : function(e){
32184                         st.cancel();
32185                     },
32186                     scope : this
32187                 };
32188             }
32189             this.el.on(this.autoHideHd);
32190         }
32191     },
32192
32193     clearAutoHide : function(){
32194         if(this.autoHide !== false){
32195             this.el.un("mouseout", this.autoHideHd.mouseout);
32196             this.el.un("mouseover", this.autoHideHd.mouseover);
32197         }
32198     },
32199
32200     clearMonitor : function(){
32201         Roo.get(document).un("click", this.slideInIf, this);
32202     },
32203
32204     // these names are backwards but not changed for compat
32205     slideOut : function(){
32206         if(this.isSlid || this.el.hasActiveFx()){
32207             return;
32208         }
32209         this.isSlid = true;
32210         if(this.collapseBtn){
32211             this.collapseBtn.hide();
32212         }
32213         this.closeBtnState = this.closeBtn.getStyle('display');
32214         this.closeBtn.hide();
32215         if(this.stickBtn){
32216             this.stickBtn.show();
32217         }
32218         this.el.show();
32219         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32220         this.beforeSlide();
32221         this.el.setStyle("z-index", 10001);
32222         this.el.slideIn(this.getSlideAnchor(), {
32223             callback: function(){
32224                 this.afterSlide();
32225                 this.initAutoHide();
32226                 Roo.get(document).on("click", this.slideInIf, this);
32227                 this.fireEvent("slideshow", this);
32228             },
32229             scope: this,
32230             block: true
32231         });
32232     },
32233
32234     afterSlideIn : function(){
32235         this.clearAutoHide();
32236         this.isSlid = false;
32237         this.clearMonitor();
32238         this.el.setStyle("z-index", "");
32239         if(this.collapseBtn){
32240             this.collapseBtn.show();
32241         }
32242         this.closeBtn.setStyle('display', this.closeBtnState);
32243         if(this.stickBtn){
32244             this.stickBtn.hide();
32245         }
32246         this.fireEvent("slidehide", this);
32247     },
32248
32249     slideIn : function(cb){
32250         if(!this.isSlid || this.el.hasActiveFx()){
32251             Roo.callback(cb);
32252             return;
32253         }
32254         this.isSlid = false;
32255         this.beforeSlide();
32256         this.el.slideOut(this.getSlideAnchor(), {
32257             callback: function(){
32258                 this.el.setLeftTop(-10000, -10000);
32259                 this.afterSlide();
32260                 this.afterSlideIn();
32261                 Roo.callback(cb);
32262             },
32263             scope: this,
32264             block: true
32265         });
32266     },
32267     
32268     slideInIf : function(e){
32269         if(!e.within(this.el)){
32270             this.slideIn();
32271         }
32272     },
32273
32274     animateCollapse : function(){
32275         this.beforeSlide();
32276         this.el.setStyle("z-index", 20000);
32277         var anchor = this.getSlideAnchor();
32278         this.el.slideOut(anchor, {
32279             callback : function(){
32280                 this.el.setStyle("z-index", "");
32281                 this.collapsedEl.slideIn(anchor, {duration:.3});
32282                 this.afterSlide();
32283                 this.el.setLocation(-10000,-10000);
32284                 this.el.hide();
32285                 this.fireEvent("collapsed", this);
32286             },
32287             scope: this,
32288             block: true
32289         });
32290     },
32291
32292     animateExpand : function(){
32293         this.beforeSlide();
32294         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
32295         this.el.setStyle("z-index", 20000);
32296         this.collapsedEl.hide({
32297             duration:.1
32298         });
32299         this.el.slideIn(this.getSlideAnchor(), {
32300             callback : function(){
32301                 this.el.setStyle("z-index", "");
32302                 this.afterSlide();
32303                 if(this.split){
32304                     this.split.el.show();
32305                 }
32306                 this.fireEvent("invalidated", this);
32307                 this.fireEvent("expanded", this);
32308             },
32309             scope: this,
32310             block: true
32311         });
32312     },
32313
32314     anchors : {
32315         "west" : "left",
32316         "east" : "right",
32317         "north" : "top",
32318         "south" : "bottom"
32319     },
32320
32321     sanchors : {
32322         "west" : "l",
32323         "east" : "r",
32324         "north" : "t",
32325         "south" : "b"
32326     },
32327
32328     canchors : {
32329         "west" : "tl-tr",
32330         "east" : "tr-tl",
32331         "north" : "tl-bl",
32332         "south" : "bl-tl"
32333     },
32334
32335     getAnchor : function(){
32336         return this.anchors[this.position];
32337     },
32338
32339     getCollapseAnchor : function(){
32340         return this.canchors[this.position];
32341     },
32342
32343     getSlideAnchor : function(){
32344         return this.sanchors[this.position];
32345     },
32346
32347     getAlignAdj : function(){
32348         var cm = this.cmargins;
32349         switch(this.position){
32350             case "west":
32351                 return [0, 0];
32352             break;
32353             case "east":
32354                 return [0, 0];
32355             break;
32356             case "north":
32357                 return [0, 0];
32358             break;
32359             case "south":
32360                 return [0, 0];
32361             break;
32362         }
32363     },
32364
32365     getExpandAdj : function(){
32366         var c = this.collapsedEl, cm = this.cmargins;
32367         switch(this.position){
32368             case "west":
32369                 return [-(cm.right+c.getWidth()+cm.left), 0];
32370             break;
32371             case "east":
32372                 return [cm.right+c.getWidth()+cm.left, 0];
32373             break;
32374             case "north":
32375                 return [0, -(cm.top+cm.bottom+c.getHeight())];
32376             break;
32377             case "south":
32378                 return [0, cm.top+cm.bottom+c.getHeight()];
32379             break;
32380         }
32381     }
32382 });/*
32383  * Based on:
32384  * Ext JS Library 1.1.1
32385  * Copyright(c) 2006-2007, Ext JS, LLC.
32386  *
32387  * Originally Released Under LGPL - original licence link has changed is not relivant.
32388  *
32389  * Fork - LGPL
32390  * <script type="text/javascript">
32391  */
32392 /*
32393  * These classes are private internal classes
32394  */
32395 Roo.CenterLayoutRegion = function(mgr, config){
32396     Roo.LayoutRegion.call(this, mgr, config, "center");
32397     this.visible = true;
32398     this.minWidth = config.minWidth || 20;
32399     this.minHeight = config.minHeight || 20;
32400 };
32401
32402 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
32403     hide : function(){
32404         // center panel can't be hidden
32405     },
32406     
32407     show : function(){
32408         // center panel can't be hidden
32409     },
32410     
32411     getMinWidth: function(){
32412         return this.minWidth;
32413     },
32414     
32415     getMinHeight: function(){
32416         return this.minHeight;
32417     }
32418 });
32419
32420
32421 Roo.NorthLayoutRegion = function(mgr, config){
32422     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
32423     if(this.split){
32424         this.split.placement = Roo.SplitBar.TOP;
32425         this.split.orientation = Roo.SplitBar.VERTICAL;
32426         this.split.el.addClass("x-layout-split-v");
32427     }
32428     var size = config.initialSize || config.height;
32429     if(typeof size != "undefined"){
32430         this.el.setHeight(size);
32431     }
32432 };
32433 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
32434     orientation: Roo.SplitBar.VERTICAL,
32435     getBox : function(){
32436         if(this.collapsed){
32437             return this.collapsedEl.getBox();
32438         }
32439         var box = this.el.getBox();
32440         if(this.split){
32441             box.height += this.split.el.getHeight();
32442         }
32443         return box;
32444     },
32445     
32446     updateBox : function(box){
32447         if(this.split && !this.collapsed){
32448             box.height -= this.split.el.getHeight();
32449             this.split.el.setLeft(box.x);
32450             this.split.el.setTop(box.y+box.height);
32451             this.split.el.setWidth(box.width);
32452         }
32453         if(this.collapsed){
32454             this.updateBody(box.width, null);
32455         }
32456         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32457     }
32458 });
32459
32460 Roo.SouthLayoutRegion = function(mgr, config){
32461     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
32462     if(this.split){
32463         this.split.placement = Roo.SplitBar.BOTTOM;
32464         this.split.orientation = Roo.SplitBar.VERTICAL;
32465         this.split.el.addClass("x-layout-split-v");
32466     }
32467     var size = config.initialSize || config.height;
32468     if(typeof size != "undefined"){
32469         this.el.setHeight(size);
32470     }
32471 };
32472 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
32473     orientation: Roo.SplitBar.VERTICAL,
32474     getBox : function(){
32475         if(this.collapsed){
32476             return this.collapsedEl.getBox();
32477         }
32478         var box = this.el.getBox();
32479         if(this.split){
32480             var sh = this.split.el.getHeight();
32481             box.height += sh;
32482             box.y -= sh;
32483         }
32484         return box;
32485     },
32486     
32487     updateBox : function(box){
32488         if(this.split && !this.collapsed){
32489             var sh = this.split.el.getHeight();
32490             box.height -= sh;
32491             box.y += sh;
32492             this.split.el.setLeft(box.x);
32493             this.split.el.setTop(box.y-sh);
32494             this.split.el.setWidth(box.width);
32495         }
32496         if(this.collapsed){
32497             this.updateBody(box.width, null);
32498         }
32499         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32500     }
32501 });
32502
32503 Roo.EastLayoutRegion = function(mgr, config){
32504     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
32505     if(this.split){
32506         this.split.placement = Roo.SplitBar.RIGHT;
32507         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32508         this.split.el.addClass("x-layout-split-h");
32509     }
32510     var size = config.initialSize || config.width;
32511     if(typeof size != "undefined"){
32512         this.el.setWidth(size);
32513     }
32514 };
32515 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
32516     orientation: Roo.SplitBar.HORIZONTAL,
32517     getBox : function(){
32518         if(this.collapsed){
32519             return this.collapsedEl.getBox();
32520         }
32521         var box = this.el.getBox();
32522         if(this.split){
32523             var sw = this.split.el.getWidth();
32524             box.width += sw;
32525             box.x -= sw;
32526         }
32527         return box;
32528     },
32529
32530     updateBox : function(box){
32531         if(this.split && !this.collapsed){
32532             var sw = this.split.el.getWidth();
32533             box.width -= sw;
32534             this.split.el.setLeft(box.x);
32535             this.split.el.setTop(box.y);
32536             this.split.el.setHeight(box.height);
32537             box.x += sw;
32538         }
32539         if(this.collapsed){
32540             this.updateBody(null, box.height);
32541         }
32542         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32543     }
32544 });
32545
32546 Roo.WestLayoutRegion = function(mgr, config){
32547     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
32548     if(this.split){
32549         this.split.placement = Roo.SplitBar.LEFT;
32550         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32551         this.split.el.addClass("x-layout-split-h");
32552     }
32553     var size = config.initialSize || config.width;
32554     if(typeof size != "undefined"){
32555         this.el.setWidth(size);
32556     }
32557 };
32558 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
32559     orientation: Roo.SplitBar.HORIZONTAL,
32560     getBox : function(){
32561         if(this.collapsed){
32562             return this.collapsedEl.getBox();
32563         }
32564         var box = this.el.getBox();
32565         if(this.split){
32566             box.width += this.split.el.getWidth();
32567         }
32568         return box;
32569     },
32570     
32571     updateBox : function(box){
32572         if(this.split && !this.collapsed){
32573             var sw = this.split.el.getWidth();
32574             box.width -= sw;
32575             this.split.el.setLeft(box.x+box.width);
32576             this.split.el.setTop(box.y);
32577             this.split.el.setHeight(box.height);
32578         }
32579         if(this.collapsed){
32580             this.updateBody(null, box.height);
32581         }
32582         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32583     }
32584 });
32585 /*
32586  * Based on:
32587  * Ext JS Library 1.1.1
32588  * Copyright(c) 2006-2007, Ext JS, LLC.
32589  *
32590  * Originally Released Under LGPL - original licence link has changed is not relivant.
32591  *
32592  * Fork - LGPL
32593  * <script type="text/javascript">
32594  */
32595  
32596  
32597 /*
32598  * Private internal class for reading and applying state
32599  */
32600 Roo.LayoutStateManager = function(layout){
32601      // default empty state
32602      this.state = {
32603         north: {},
32604         south: {},
32605         east: {},
32606         west: {}       
32607     };
32608 };
32609
32610 Roo.LayoutStateManager.prototype = {
32611     init : function(layout, provider){
32612         this.provider = provider;
32613         var state = provider.get(layout.id+"-layout-state");
32614         if(state){
32615             var wasUpdating = layout.isUpdating();
32616             if(!wasUpdating){
32617                 layout.beginUpdate();
32618             }
32619             for(var key in state){
32620                 if(typeof state[key] != "function"){
32621                     var rstate = state[key];
32622                     var r = layout.getRegion(key);
32623                     if(r && rstate){
32624                         if(rstate.size){
32625                             r.resizeTo(rstate.size);
32626                         }
32627                         if(rstate.collapsed == true){
32628                             r.collapse(true);
32629                         }else{
32630                             r.expand(null, true);
32631                         }
32632                     }
32633                 }
32634             }
32635             if(!wasUpdating){
32636                 layout.endUpdate();
32637             }
32638             this.state = state; 
32639         }
32640         this.layout = layout;
32641         layout.on("regionresized", this.onRegionResized, this);
32642         layout.on("regioncollapsed", this.onRegionCollapsed, this);
32643         layout.on("regionexpanded", this.onRegionExpanded, this);
32644     },
32645     
32646     storeState : function(){
32647         this.provider.set(this.layout.id+"-layout-state", this.state);
32648     },
32649     
32650     onRegionResized : function(region, newSize){
32651         this.state[region.getPosition()].size = newSize;
32652         this.storeState();
32653     },
32654     
32655     onRegionCollapsed : function(region){
32656         this.state[region.getPosition()].collapsed = true;
32657         this.storeState();
32658     },
32659     
32660     onRegionExpanded : function(region){
32661         this.state[region.getPosition()].collapsed = false;
32662         this.storeState();
32663     }
32664 };/*
32665  * Based on:
32666  * Ext JS Library 1.1.1
32667  * Copyright(c) 2006-2007, Ext JS, LLC.
32668  *
32669  * Originally Released Under LGPL - original licence link has changed is not relivant.
32670  *
32671  * Fork - LGPL
32672  * <script type="text/javascript">
32673  */
32674 /**
32675  * @class Roo.ContentPanel
32676  * @extends Roo.util.Observable
32677  * A basic ContentPanel element.
32678  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
32679  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
32680  * @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
32681  * @cfg {Boolean}   closable      True if the panel can be closed/removed
32682  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
32683  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
32684  * @cfg {Toolbar}   toolbar       A toolbar for this panel
32685  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
32686  * @cfg {String} title          The title for this panel
32687  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
32688  * @cfg {String} url            Calls {@link #setUrl} with this value
32689  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
32690  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
32691  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
32692  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
32693
32694  * @constructor
32695  * Create a new ContentPanel.
32696  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
32697  * @param {String/Object} config A string to set only the title or a config object
32698  * @param {String} content (optional) Set the HTML content for this panel
32699  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
32700  */
32701 Roo.ContentPanel = function(el, config, content){
32702     
32703      
32704     /*
32705     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
32706         config = el;
32707         el = Roo.id();
32708     }
32709     if (config && config.parentLayout) { 
32710         el = config.parentLayout.el.createChild(); 
32711     }
32712     */
32713     if(el.autoCreate){ // xtype is available if this is called from factory
32714         config = el;
32715         el = Roo.id();
32716     }
32717     this.el = Roo.get(el);
32718     if(!this.el && config && config.autoCreate){
32719         if(typeof config.autoCreate == "object"){
32720             if(!config.autoCreate.id){
32721                 config.autoCreate.id = config.id||el;
32722             }
32723             this.el = Roo.DomHelper.append(document.body,
32724                         config.autoCreate, true);
32725         }else{
32726             this.el = Roo.DomHelper.append(document.body,
32727                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
32728         }
32729     }
32730     this.closable = false;
32731     this.loaded = false;
32732     this.active = false;
32733     if(typeof config == "string"){
32734         this.title = config;
32735     }else{
32736         Roo.apply(this, config);
32737     }
32738     
32739     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
32740         this.wrapEl = this.el.wrap();
32741         this.toolbar.container = this.el.insertSibling(false, 'before');
32742         this.toolbar = new Roo.Toolbar(this.toolbar);
32743     }
32744     
32745     
32746     
32747     if(this.resizeEl){
32748         this.resizeEl = Roo.get(this.resizeEl, true);
32749     }else{
32750         this.resizeEl = this.el;
32751     }
32752     this.addEvents({
32753         /**
32754          * @event activate
32755          * Fires when this panel is activated. 
32756          * @param {Roo.ContentPanel} this
32757          */
32758         "activate" : true,
32759         /**
32760          * @event deactivate
32761          * Fires when this panel is activated. 
32762          * @param {Roo.ContentPanel} this
32763          */
32764         "deactivate" : true,
32765
32766         /**
32767          * @event resize
32768          * Fires when this panel is resized if fitToFrame is true.
32769          * @param {Roo.ContentPanel} this
32770          * @param {Number} width The width after any component adjustments
32771          * @param {Number} height The height after any component adjustments
32772          */
32773         "resize" : true,
32774         
32775          /**
32776          * @event render
32777          * Fires when this tab is created
32778          * @param {Roo.ContentPanel} this
32779          */
32780         "render" : true
32781         
32782         
32783         
32784     });
32785     if(this.autoScroll){
32786         this.resizeEl.setStyle("overflow", "auto");
32787     } else {
32788         // fix randome scrolling
32789         this.el.on('scroll', function() {
32790             Roo.log('fix random scolling');
32791             this.scrollTo('top',0); 
32792         });
32793     }
32794     content = content || this.content;
32795     if(content){
32796         this.setContent(content);
32797     }
32798     if(config && config.url){
32799         this.setUrl(this.url, this.params, this.loadOnce);
32800     }
32801     
32802     
32803     
32804     Roo.ContentPanel.superclass.constructor.call(this);
32805     
32806     this.fireEvent('render', this);
32807 };
32808
32809 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
32810     tabTip:'',
32811     setRegion : function(region){
32812         this.region = region;
32813         if(region){
32814            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
32815         }else{
32816            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
32817         } 
32818     },
32819     
32820     /**
32821      * Returns the toolbar for this Panel if one was configured. 
32822      * @return {Roo.Toolbar} 
32823      */
32824     getToolbar : function(){
32825         return this.toolbar;
32826     },
32827     
32828     setActiveState : function(active){
32829         this.active = active;
32830         if(!active){
32831             this.fireEvent("deactivate", this);
32832         }else{
32833             this.fireEvent("activate", this);
32834         }
32835     },
32836     /**
32837      * Updates this panel's element
32838      * @param {String} content The new content
32839      * @param {Boolean} loadScripts (optional) true to look for and process scripts
32840     */
32841     setContent : function(content, loadScripts){
32842         this.el.update(content, loadScripts);
32843     },
32844
32845     ignoreResize : function(w, h){
32846         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
32847             return true;
32848         }else{
32849             this.lastSize = {width: w, height: h};
32850             return false;
32851         }
32852     },
32853     /**
32854      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
32855      * @return {Roo.UpdateManager} The UpdateManager
32856      */
32857     getUpdateManager : function(){
32858         return this.el.getUpdateManager();
32859     },
32860      /**
32861      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
32862      * @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:
32863 <pre><code>
32864 panel.load({
32865     url: "your-url.php",
32866     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
32867     callback: yourFunction,
32868     scope: yourObject, //(optional scope)
32869     discardUrl: false,
32870     nocache: false,
32871     text: "Loading...",
32872     timeout: 30,
32873     scripts: false
32874 });
32875 </code></pre>
32876      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
32877      * are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
32878      * @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}
32879      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
32880      * @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.
32881      * @return {Roo.ContentPanel} this
32882      */
32883     load : function(){
32884         var um = this.el.getUpdateManager();
32885         um.update.apply(um, arguments);
32886         return this;
32887     },
32888
32889
32890     /**
32891      * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
32892      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
32893      * @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)
32894      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
32895      * @return {Roo.UpdateManager} The UpdateManager
32896      */
32897     setUrl : function(url, params, loadOnce){
32898         if(this.refreshDelegate){
32899             this.removeListener("activate", this.refreshDelegate);
32900         }
32901         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
32902         this.on("activate", this.refreshDelegate);
32903         return this.el.getUpdateManager();
32904     },
32905     
32906     _handleRefresh : function(url, params, loadOnce){
32907         if(!loadOnce || !this.loaded){
32908             var updater = this.el.getUpdateManager();
32909             updater.update(url, params, this._setLoaded.createDelegate(this));
32910         }
32911     },
32912     
32913     _setLoaded : function(){
32914         this.loaded = true;
32915     }, 
32916     
32917     /**
32918      * Returns this panel's id
32919      * @return {String} 
32920      */
32921     getId : function(){
32922         return this.el.id;
32923     },
32924     
32925     /** 
32926      * Returns this panel's element - used by regiosn to add.
32927      * @return {Roo.Element} 
32928      */
32929     getEl : function(){
32930         return this.wrapEl || this.el;
32931     },
32932     
32933     adjustForComponents : function(width, height){
32934         if(this.resizeEl != this.el){
32935             width -= this.el.getFrameWidth('lr');
32936             height -= this.el.getFrameWidth('tb');
32937         }
32938         if(this.toolbar){
32939             var te = this.toolbar.getEl();
32940             height -= te.getHeight();
32941             te.setWidth(width);
32942         }
32943         if(this.adjustments){
32944             width += this.adjustments[0];
32945             height += this.adjustments[1];
32946         }
32947         return {"width": width, "height": height};
32948     },
32949     
32950     setSize : function(width, height){
32951         if(this.fitToFrame && !this.ignoreResize(width, height)){
32952             if(this.fitContainer && this.resizeEl != this.el){
32953                 this.el.setSize(width, height);
32954             }
32955             var size = this.adjustForComponents(width, height);
32956             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
32957             this.fireEvent('resize', this, size.width, size.height);
32958         }
32959     },
32960     
32961     /**
32962      * Returns this panel's title
32963      * @return {String} 
32964      */
32965     getTitle : function(){
32966         return this.title;
32967     },
32968     
32969     /**
32970      * Set this panel's title
32971      * @param {String} title
32972      */
32973     setTitle : function(title){
32974         this.title = title;
32975         if(this.region){
32976             this.region.updatePanelTitle(this, title);
32977         }
32978     },
32979     
32980     /**
32981      * Returns true is this panel was configured to be closable
32982      * @return {Boolean} 
32983      */
32984     isClosable : function(){
32985         return this.closable;
32986     },
32987     
32988     beforeSlide : function(){
32989         this.el.clip();
32990         this.resizeEl.clip();
32991     },
32992     
32993     afterSlide : function(){
32994         this.el.unclip();
32995         this.resizeEl.unclip();
32996     },
32997     
32998     /**
32999      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
33000      *   Will fail silently if the {@link #setUrl} method has not been called.
33001      *   This does not activate the panel, just updates its content.
33002      */
33003     refresh : function(){
33004         if(this.refreshDelegate){
33005            this.loaded = false;
33006            this.refreshDelegate();
33007         }
33008     },
33009     
33010     /**
33011      * Destroys this panel
33012      */
33013     destroy : function(){
33014         this.el.removeAllListeners();
33015         var tempEl = document.createElement("span");
33016         tempEl.appendChild(this.el.dom);
33017         tempEl.innerHTML = "";
33018         this.el.remove();
33019         this.el = null;
33020     },
33021     
33022     /**
33023      * form - if the content panel contains a form - this is a reference to it.
33024      * @type {Roo.form.Form}
33025      */
33026     form : false,
33027     /**
33028      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33029      *    This contains a reference to it.
33030      * @type {Roo.View}
33031      */
33032     view : false,
33033     
33034       /**
33035      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33036      * <pre><code>
33037
33038 layout.addxtype({
33039        xtype : 'Form',
33040        items: [ .... ]
33041    }
33042 );
33043
33044 </code></pre>
33045      * @param {Object} cfg Xtype definition of item to add.
33046      */
33047     
33048     addxtype : function(cfg) {
33049         // add form..
33050         if (cfg.xtype.match(/^Form$/)) {
33051             var el = this.el.createChild();
33052
33053             this.form = new  Roo.form.Form(cfg);
33054             
33055             
33056             if ( this.form.allItems.length) this.form.render(el.dom);
33057             return this.form;
33058         }
33059         // should only have one of theses..
33060         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33061             // views..
33062             cfg.el = this.el.appendChild(document.createElement("div"));
33063             // factory?
33064             
33065             var ret = new Roo.factory(cfg);
33066             ret.render && ret.render(false, ''); // render blank..
33067             this.view = ret;
33068             return ret;
33069         }
33070         return false;
33071     }
33072 });
33073
33074 /**
33075  * @class Roo.GridPanel
33076  * @extends Roo.ContentPanel
33077  * @constructor
33078  * Create a new GridPanel.
33079  * @param {Roo.grid.Grid} grid The grid for this panel
33080  * @param {String/Object} config A string to set only the panel's title, or a config object
33081  */
33082 Roo.GridPanel = function(grid, config){
33083     
33084   
33085     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33086         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
33087         
33088     this.wrapper.dom.appendChild(grid.getGridEl().dom);
33089     
33090     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
33091     
33092     if(this.toolbar){
33093         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
33094     }
33095     // xtype created footer. - not sure if will work as we normally have to render first..
33096     if (this.footer && !this.footer.el && this.footer.xtype) {
33097         
33098         this.footer.container = this.grid.getView().getFooterPanel(true);
33099         this.footer.dataSource = this.grid.dataSource;
33100         this.footer = Roo.factory(this.footer, Roo);
33101         
33102     }
33103     
33104     grid.monitorWindowResize = false; // turn off autosizing
33105     grid.autoHeight = false;
33106     grid.autoWidth = false;
33107     this.grid = grid;
33108     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
33109 };
33110
33111 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
33112     getId : function(){
33113         return this.grid.id;
33114     },
33115     
33116     /**
33117      * Returns the grid for this panel
33118      * @return {Roo.grid.Grid} 
33119      */
33120     getGrid : function(){
33121         return this.grid;    
33122     },
33123     
33124     setSize : function(width, height){
33125         if(!this.ignoreResize(width, height)){
33126             var grid = this.grid;
33127             var size = this.adjustForComponents(width, height);
33128             grid.getGridEl().setSize(size.width, size.height);
33129             grid.autoSize();
33130         }
33131     },
33132     
33133     beforeSlide : function(){
33134         this.grid.getView().scroller.clip();
33135     },
33136     
33137     afterSlide : function(){
33138         this.grid.getView().scroller.unclip();
33139     },
33140     
33141     destroy : function(){
33142         this.grid.destroy();
33143         delete this.grid;
33144         Roo.GridPanel.superclass.destroy.call(this); 
33145     }
33146 });
33147
33148
33149 /**
33150  * @class Roo.NestedLayoutPanel
33151  * @extends Roo.ContentPanel
33152  * @constructor
33153  * Create a new NestedLayoutPanel.
33154  * 
33155  * 
33156  * @param {Roo.BorderLayout} layout The layout for this panel
33157  * @param {String/Object} config A string to set only the title or a config object
33158  */
33159 Roo.NestedLayoutPanel = function(layout, config)
33160 {
33161     // construct with only one argument..
33162     /* FIXME - implement nicer consturctors
33163     if (layout.layout) {
33164         config = layout;
33165         layout = config.layout;
33166         delete config.layout;
33167     }
33168     if (layout.xtype && !layout.getEl) {
33169         // then layout needs constructing..
33170         layout = Roo.factory(layout, Roo);
33171     }
33172     */
33173     
33174     
33175     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
33176     
33177     layout.monitorWindowResize = false; // turn off autosizing
33178     this.layout = layout;
33179     this.layout.getEl().addClass("x-layout-nested-layout");
33180     
33181     
33182     
33183     
33184 };
33185
33186 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
33187
33188     setSize : function(width, height){
33189         if(!this.ignoreResize(width, height)){
33190             var size = this.adjustForComponents(width, height);
33191             var el = this.layout.getEl();
33192             el.setSize(size.width, size.height);
33193             var touch = el.dom.offsetWidth;
33194             this.layout.layout();
33195             // ie requires a double layout on the first pass
33196             if(Roo.isIE && !this.initialized){
33197                 this.initialized = true;
33198                 this.layout.layout();
33199             }
33200         }
33201     },
33202     
33203     // activate all subpanels if not currently active..
33204     
33205     setActiveState : function(active){
33206         this.active = active;
33207         if(!active){
33208             this.fireEvent("deactivate", this);
33209             return;
33210         }
33211         
33212         this.fireEvent("activate", this);
33213         // not sure if this should happen before or after..
33214         if (!this.layout) {
33215             return; // should not happen..
33216         }
33217         var reg = false;
33218         for (var r in this.layout.regions) {
33219             reg = this.layout.getRegion(r);
33220             if (reg.getActivePanel()) {
33221                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
33222                 reg.setActivePanel(reg.getActivePanel());
33223                 continue;
33224             }
33225             if (!reg.panels.length) {
33226                 continue;
33227             }
33228             reg.showPanel(reg.getPanel(0));
33229         }
33230         
33231         
33232         
33233         
33234     },
33235     
33236     /**
33237      * Returns the nested BorderLayout for this panel
33238      * @return {Roo.BorderLayout} 
33239      */
33240     getLayout : function(){
33241         return this.layout;
33242     },
33243     
33244      /**
33245      * Adds a xtype elements to the layout of the nested panel
33246      * <pre><code>
33247
33248 panel.addxtype({
33249        xtype : 'ContentPanel',
33250        region: 'west',
33251        items: [ .... ]
33252    }
33253 );
33254
33255 panel.addxtype({
33256         xtype : 'NestedLayoutPanel',
33257         region: 'west',
33258         layout: {
33259            center: { },
33260            west: { }   
33261         },
33262         items : [ ... list of content panels or nested layout panels.. ]
33263    }
33264 );
33265 </code></pre>
33266      * @param {Object} cfg Xtype definition of item to add.
33267      */
33268     addxtype : function(cfg) {
33269         return this.layout.addxtype(cfg);
33270     
33271     }
33272 });
33273
33274 Roo.ScrollPanel = function(el, config, content){
33275     config = config || {};
33276     config.fitToFrame = true;
33277     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
33278     
33279     this.el.dom.style.overflow = "hidden";
33280     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
33281     this.el.removeClass("x-layout-inactive-content");
33282     this.el.on("mousewheel", this.onWheel, this);
33283
33284     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
33285     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
33286     up.unselectable(); down.unselectable();
33287     up.on("click", this.scrollUp, this);
33288     down.on("click", this.scrollDown, this);
33289     up.addClassOnOver("x-scroller-btn-over");
33290     down.addClassOnOver("x-scroller-btn-over");
33291     up.addClassOnClick("x-scroller-btn-click");
33292     down.addClassOnClick("x-scroller-btn-click");
33293     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
33294
33295     this.resizeEl = this.el;
33296     this.el = wrap; this.up = up; this.down = down;
33297 };
33298
33299 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
33300     increment : 100,
33301     wheelIncrement : 5,
33302     scrollUp : function(){
33303         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
33304     },
33305
33306     scrollDown : function(){
33307         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
33308     },
33309
33310     afterScroll : function(){
33311         var el = this.resizeEl;
33312         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
33313         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33314         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33315     },
33316
33317     setSize : function(){
33318         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
33319         this.afterScroll();
33320     },
33321
33322     onWheel : function(e){
33323         var d = e.getWheelDelta();
33324         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
33325         this.afterScroll();
33326         e.stopEvent();
33327     },
33328
33329     setContent : function(content, loadScripts){
33330         this.resizeEl.update(content, loadScripts);
33331     }
33332
33333 });
33334
33335
33336
33337
33338
33339
33340
33341
33342
33343 /**
33344  * @class Roo.TreePanel
33345  * @extends Roo.ContentPanel
33346  * @constructor
33347  * Create a new TreePanel. - defaults to fit/scoll contents.
33348  * @param {String/Object} config A string to set only the panel's title, or a config object
33349  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
33350  */
33351 Roo.TreePanel = function(config){
33352     var el = config.el;
33353     var tree = config.tree;
33354     delete config.tree; 
33355     delete config.el; // hopefull!
33356     
33357     // wrapper for IE7 strict & safari scroll issue
33358     
33359     var treeEl = el.createChild();
33360     config.resizeEl = treeEl;
33361     
33362     
33363     
33364     Roo.TreePanel.superclass.constructor.call(this, el, config);
33365  
33366  
33367     this.tree = new Roo.tree.TreePanel(treeEl , tree);
33368     //console.log(tree);
33369     this.on('activate', function()
33370     {
33371         if (this.tree.rendered) {
33372             return;
33373         }
33374         //console.log('render tree');
33375         this.tree.render();
33376     });
33377     
33378     this.on('resize',  function (cp, w, h) {
33379             this.tree.innerCt.setWidth(w);
33380             this.tree.innerCt.setHeight(h);
33381             this.tree.innerCt.setStyle('overflow-y', 'auto');
33382     });
33383
33384         
33385     
33386 };
33387
33388 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
33389     fitToFrame : true,
33390     autoScroll : true
33391 });
33392
33393
33394
33395
33396
33397
33398
33399
33400
33401
33402
33403 /*
33404  * Based on:
33405  * Ext JS Library 1.1.1
33406  * Copyright(c) 2006-2007, Ext JS, LLC.
33407  *
33408  * Originally Released Under LGPL - original licence link has changed is not relivant.
33409  *
33410  * Fork - LGPL
33411  * <script type="text/javascript">
33412  */
33413  
33414
33415 /**
33416  * @class Roo.ReaderLayout
33417  * @extends Roo.BorderLayout
33418  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
33419  * center region containing two nested regions (a top one for a list view and one for item preview below),
33420  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
33421  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
33422  * expedites the setup of the overall layout and regions for this common application style.
33423  * Example:
33424  <pre><code>
33425 var reader = new Roo.ReaderLayout();
33426 var CP = Roo.ContentPanel;  // shortcut for adding
33427
33428 reader.beginUpdate();
33429 reader.add("north", new CP("north", "North"));
33430 reader.add("west", new CP("west", {title: "West"}));
33431 reader.add("east", new CP("east", {title: "East"}));
33432
33433 reader.regions.listView.add(new CP("listView", "List"));
33434 reader.regions.preview.add(new CP("preview", "Preview"));
33435 reader.endUpdate();
33436 </code></pre>
33437 * @constructor
33438 * Create a new ReaderLayout
33439 * @param {Object} config Configuration options
33440 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
33441 * document.body if omitted)
33442 */
33443 Roo.ReaderLayout = function(config, renderTo){
33444     var c = config || {size:{}};
33445     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
33446         north: c.north !== false ? Roo.apply({
33447             split:false,
33448             initialSize: 32,
33449             titlebar: false
33450         }, c.north) : false,
33451         west: c.west !== false ? Roo.apply({
33452             split:true,
33453             initialSize: 200,
33454             minSize: 175,
33455             maxSize: 400,
33456             titlebar: true,
33457             collapsible: true,
33458             animate: true,
33459             margins:{left:5,right:0,bottom:5,top:5},
33460             cmargins:{left:5,right:5,bottom:5,top:5}
33461         }, c.west) : false,
33462         east: c.east !== false ? Roo.apply({
33463             split:true,
33464             initialSize: 200,
33465             minSize: 175,
33466             maxSize: 400,
33467             titlebar: true,
33468             collapsible: true,
33469             animate: true,
33470             margins:{left:0,right:5,bottom:5,top:5},
33471             cmargins:{left:5,right:5,bottom:5,top:5}
33472         }, c.east) : false,
33473         center: Roo.apply({
33474             tabPosition: 'top',
33475             autoScroll:false,
33476             closeOnTab: true,
33477             titlebar:false,
33478             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
33479         }, c.center)
33480     });
33481
33482     this.el.addClass('x-reader');
33483
33484     this.beginUpdate();
33485
33486     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
33487         south: c.preview !== false ? Roo.apply({
33488             split:true,
33489             initialSize: 200,
33490             minSize: 100,
33491             autoScroll:true,
33492             collapsible:true,
33493             titlebar: true,
33494             cmargins:{top:5,left:0, right:0, bottom:0}
33495         }, c.preview) : false,
33496         center: Roo.apply({
33497             autoScroll:false,
33498             titlebar:false,
33499             minHeight:200
33500         }, c.listView)
33501     });
33502     this.add('center', new Roo.NestedLayoutPanel(inner,
33503             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
33504
33505     this.endUpdate();
33506
33507     this.regions.preview = inner.getRegion('south');
33508     this.regions.listView = inner.getRegion('center');
33509 };
33510
33511 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
33512  * Based on:
33513  * Ext JS Library 1.1.1
33514  * Copyright(c) 2006-2007, Ext JS, LLC.
33515  *
33516  * Originally Released Under LGPL - original licence link has changed is not relivant.
33517  *
33518  * Fork - LGPL
33519  * <script type="text/javascript">
33520  */
33521  
33522 /**
33523  * @class Roo.grid.Grid
33524  * @extends Roo.util.Observable
33525  * This class represents the primary interface of a component based grid control.
33526  * <br><br>Usage:<pre><code>
33527  var grid = new Roo.grid.Grid("my-container-id", {
33528      ds: myDataStore,
33529      cm: myColModel,
33530      selModel: mySelectionModel,
33531      autoSizeColumns: true,
33532      monitorWindowResize: false,
33533      trackMouseOver: true
33534  });
33535  // set any options
33536  grid.render();
33537  * </code></pre>
33538  * <b>Common Problems:</b><br/>
33539  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
33540  * element will correct this<br/>
33541  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
33542  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
33543  * are unpredictable.<br/>
33544  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
33545  * grid to calculate dimensions/offsets.<br/>
33546   * @constructor
33547  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
33548  * The container MUST have some type of size defined for the grid to fill. The container will be
33549  * automatically set to position relative if it isn't already.
33550  * @param {Object} config A config object that sets properties on this grid.
33551  */
33552 Roo.grid.Grid = function(container, config){
33553         // initialize the container
33554         this.container = Roo.get(container);
33555         this.container.update("");
33556         this.container.setStyle("overflow", "hidden");
33557     this.container.addClass('x-grid-container');
33558
33559     this.id = this.container.id;
33560
33561     Roo.apply(this, config);
33562     // check and correct shorthanded configs
33563     if(this.ds){
33564         this.dataSource = this.ds;
33565         delete this.ds;
33566     }
33567     if(this.cm){
33568         this.colModel = this.cm;
33569         delete this.cm;
33570     }
33571     if(this.sm){
33572         this.selModel = this.sm;
33573         delete this.sm;
33574     }
33575
33576     if (this.selModel) {
33577         this.selModel = Roo.factory(this.selModel, Roo.grid);
33578         this.sm = this.selModel;
33579         this.sm.xmodule = this.xmodule || false;
33580     }
33581     if (typeof(this.colModel.config) == 'undefined') {
33582         this.colModel = new Roo.grid.ColumnModel(this.colModel);
33583         this.cm = this.colModel;
33584         this.cm.xmodule = this.xmodule || false;
33585     }
33586     if (this.dataSource) {
33587         this.dataSource= Roo.factory(this.dataSource, Roo.data);
33588         this.ds = this.dataSource;
33589         this.ds.xmodule = this.xmodule || false;
33590          
33591     }
33592     
33593     
33594     
33595     if(this.width){
33596         this.container.setWidth(this.width);
33597     }
33598
33599     if(this.height){
33600         this.container.setHeight(this.height);
33601     }
33602     /** @private */
33603         this.addEvents({
33604         // raw events
33605         /**
33606          * @event click
33607          * The raw click event for the entire grid.
33608          * @param {Roo.EventObject} e
33609          */
33610         "click" : true,
33611         /**
33612          * @event dblclick
33613          * The raw dblclick event for the entire grid.
33614          * @param {Roo.EventObject} e
33615          */
33616         "dblclick" : true,
33617         /**
33618          * @event contextmenu
33619          * The raw contextmenu event for the entire grid.
33620          * @param {Roo.EventObject} e
33621          */
33622         "contextmenu" : true,
33623         /**
33624          * @event mousedown
33625          * The raw mousedown event for the entire grid.
33626          * @param {Roo.EventObject} e
33627          */
33628         "mousedown" : true,
33629         /**
33630          * @event mouseup
33631          * The raw mouseup event for the entire grid.
33632          * @param {Roo.EventObject} e
33633          */
33634         "mouseup" : true,
33635         /**
33636          * @event mouseover
33637          * The raw mouseover event for the entire grid.
33638          * @param {Roo.EventObject} e
33639          */
33640         "mouseover" : true,
33641         /**
33642          * @event mouseout
33643          * The raw mouseout event for the entire grid.
33644          * @param {Roo.EventObject} e
33645          */
33646         "mouseout" : true,
33647         /**
33648          * @event keypress
33649          * The raw keypress event for the entire grid.
33650          * @param {Roo.EventObject} e
33651          */
33652         "keypress" : true,
33653         /**
33654          * @event keydown
33655          * The raw keydown event for the entire grid.
33656          * @param {Roo.EventObject} e
33657          */
33658         "keydown" : true,
33659
33660         // custom events
33661
33662         /**
33663          * @event cellclick
33664          * Fires when a cell is clicked
33665          * @param {Grid} this
33666          * @param {Number} rowIndex
33667          * @param {Number} columnIndex
33668          * @param {Roo.EventObject} e
33669          */
33670         "cellclick" : true,
33671         /**
33672          * @event celldblclick
33673          * Fires when a cell is double clicked
33674          * @param {Grid} this
33675          * @param {Number} rowIndex
33676          * @param {Number} columnIndex
33677          * @param {Roo.EventObject} e
33678          */
33679         "celldblclick" : true,
33680         /**
33681          * @event rowclick
33682          * Fires when a row is clicked
33683          * @param {Grid} this
33684          * @param {Number} rowIndex
33685          * @param {Roo.EventObject} e
33686          */
33687         "rowclick" : true,
33688         /**
33689          * @event rowdblclick
33690          * Fires when a row is double clicked
33691          * @param {Grid} this
33692          * @param {Number} rowIndex
33693          * @param {Roo.EventObject} e
33694          */
33695         "rowdblclick" : true,
33696         /**
33697          * @event headerclick
33698          * Fires when a header is clicked
33699          * @param {Grid} this
33700          * @param {Number} columnIndex
33701          * @param {Roo.EventObject} e
33702          */
33703         "headerclick" : true,
33704         /**
33705          * @event headerdblclick
33706          * Fires when a header cell is double clicked
33707          * @param {Grid} this
33708          * @param {Number} columnIndex
33709          * @param {Roo.EventObject} e
33710          */
33711         "headerdblclick" : true,
33712         /**
33713          * @event rowcontextmenu
33714          * Fires when a row is right clicked
33715          * @param {Grid} this
33716          * @param {Number} rowIndex
33717          * @param {Roo.EventObject} e
33718          */
33719         "rowcontextmenu" : true,
33720         /**
33721          * @event cellcontextmenu
33722          * Fires when a cell is right clicked
33723          * @param {Grid} this
33724          * @param {Number} rowIndex
33725          * @param {Number} cellIndex
33726          * @param {Roo.EventObject} e
33727          */
33728          "cellcontextmenu" : true,
33729         /**
33730          * @event headercontextmenu
33731          * Fires when a header is right clicked
33732          * @param {Grid} this
33733          * @param {Number} columnIndex
33734          * @param {Roo.EventObject} e
33735          */
33736         "headercontextmenu" : true,
33737         /**
33738          * @event bodyscroll
33739          * Fires when the body element is scrolled
33740          * @param {Number} scrollLeft
33741          * @param {Number} scrollTop
33742          */
33743         "bodyscroll" : true,
33744         /**
33745          * @event columnresize
33746          * Fires when the user resizes a column
33747          * @param {Number} columnIndex
33748          * @param {Number} newSize
33749          */
33750         "columnresize" : true,
33751         /**
33752          * @event columnmove
33753          * Fires when the user moves a column
33754          * @param {Number} oldIndex
33755          * @param {Number} newIndex
33756          */
33757         "columnmove" : true,
33758         /**
33759          * @event startdrag
33760          * Fires when row(s) start being dragged
33761          * @param {Grid} this
33762          * @param {Roo.GridDD} dd The drag drop object
33763          * @param {event} e The raw browser event
33764          */
33765         "startdrag" : true,
33766         /**
33767          * @event enddrag
33768          * Fires when a drag operation is complete
33769          * @param {Grid} this
33770          * @param {Roo.GridDD} dd The drag drop object
33771          * @param {event} e The raw browser event
33772          */
33773         "enddrag" : true,
33774         /**
33775          * @event dragdrop
33776          * Fires when dragged row(s) are dropped on a valid DD target
33777          * @param {Grid} this
33778          * @param {Roo.GridDD} dd The drag drop object
33779          * @param {String} targetId The target drag drop object
33780          * @param {event} e The raw browser event
33781          */
33782         "dragdrop" : true,
33783         /**
33784          * @event dragover
33785          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
33786          * @param {Grid} this
33787          * @param {Roo.GridDD} dd The drag drop object
33788          * @param {String} targetId The target drag drop object
33789          * @param {event} e The raw browser event
33790          */
33791         "dragover" : true,
33792         /**
33793          * @event dragenter
33794          *  Fires when the dragged row(s) first cross another DD target while being dragged
33795          * @param {Grid} this
33796          * @param {Roo.GridDD} dd The drag drop object
33797          * @param {String} targetId The target drag drop object
33798          * @param {event} e The raw browser event
33799          */
33800         "dragenter" : true,
33801         /**
33802          * @event dragout
33803          * Fires when the dragged row(s) leave another DD target while being dragged
33804          * @param {Grid} this
33805          * @param {Roo.GridDD} dd The drag drop object
33806          * @param {String} targetId The target drag drop object
33807          * @param {event} e The raw browser event
33808          */
33809         "dragout" : true,
33810         /**
33811          * @event rowclass
33812          * Fires when a row is rendered, so you can change add a style to it.
33813          * @param {GridView} gridview   The grid view
33814          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
33815          */
33816         'rowclass' : true,
33817
33818         /**
33819          * @event render
33820          * Fires when the grid is rendered
33821          * @param {Grid} grid
33822          */
33823         'render' : true
33824     });
33825
33826     Roo.grid.Grid.superclass.constructor.call(this);
33827 };
33828 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
33829     
33830     /**
33831      * @cfg {String} ddGroup - drag drop group.
33832      */
33833
33834     /**
33835      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
33836      */
33837     minColumnWidth : 25,
33838
33839     /**
33840      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
33841      * <b>on initial render.</b> It is more efficient to explicitly size the columns
33842      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
33843      */
33844     autoSizeColumns : false,
33845
33846     /**
33847      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
33848      */
33849     autoSizeHeaders : true,
33850
33851     /**
33852      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
33853      */
33854     monitorWindowResize : true,
33855
33856     /**
33857      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
33858      * rows measured to get a columns size. Default is 0 (all rows).
33859      */
33860     maxRowsToMeasure : 0,
33861
33862     /**
33863      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
33864      */
33865     trackMouseOver : true,
33866
33867     /**
33868     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
33869     */
33870     
33871     /**
33872     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
33873     */
33874     enableDragDrop : false,
33875     
33876     /**
33877     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
33878     */
33879     enableColumnMove : true,
33880     
33881     /**
33882     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
33883     */
33884     enableColumnHide : true,
33885     
33886     /**
33887     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
33888     */
33889     enableRowHeightSync : false,
33890     
33891     /**
33892     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
33893     */
33894     stripeRows : true,
33895     
33896     /**
33897     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
33898     */
33899     autoHeight : false,
33900
33901     /**
33902      * @cfg {String} autoExpandColumn The id (or dataIndex) of a column in this grid that should expand to fill unused space. This id can not be 0. Default is false.
33903      */
33904     autoExpandColumn : false,
33905
33906     /**
33907     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
33908     * Default is 50.
33909     */
33910     autoExpandMin : 50,
33911
33912     /**
33913     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
33914     */
33915     autoExpandMax : 1000,
33916
33917     /**
33918     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
33919     */
33920     view : null,
33921
33922     /**
33923     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
33924     */
33925     loadMask : false,
33926     /**
33927     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
33928     */
33929     dropTarget: false,
33930     
33931    
33932     
33933     // private
33934     rendered : false,
33935
33936     /**
33937     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
33938     * of a fixed width. Default is false.
33939     */
33940     /**
33941     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
33942     */
33943     /**
33944      * Called once after all setup has been completed and the grid is ready to be rendered.
33945      * @return {Roo.grid.Grid} this
33946      */
33947     render : function()
33948     {
33949         var c = this.container;
33950         // try to detect autoHeight/width mode
33951         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
33952             this.autoHeight = true;
33953         }
33954         var view = this.getView();
33955         view.init(this);
33956
33957         c.on("click", this.onClick, this);
33958         c.on("dblclick", this.onDblClick, this);
33959         c.on("contextmenu", this.onContextMenu, this);
33960         c.on("keydown", this.onKeyDown, this);
33961
33962         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
33963
33964         this.getSelectionModel().init(this);
33965
33966         view.render();
33967
33968         if(this.loadMask){
33969             this.loadMask = new Roo.LoadMask(this.container,
33970                     Roo.apply({store:this.dataSource}, this.loadMask));
33971         }
33972         
33973         
33974         if (this.toolbar && this.toolbar.xtype) {
33975             this.toolbar.container = this.getView().getHeaderPanel(true);
33976             this.toolbar = new Roo.Toolbar(this.toolbar);
33977         }
33978         if (this.footer && this.footer.xtype) {
33979             this.footer.dataSource = this.getDataSource();
33980             this.footer.container = this.getView().getFooterPanel(true);
33981             this.footer = Roo.factory(this.footer, Roo);
33982         }
33983         if (this.dropTarget && this.dropTarget.xtype) {
33984             delete this.dropTarget.xtype;
33985             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
33986         }
33987         
33988         
33989         this.rendered = true;
33990         this.fireEvent('render', this);
33991         return this;
33992     },
33993
33994         /**
33995          * Reconfigures the grid to use a different Store and Column Model.
33996          * The View will be bound to the new objects and refreshed.
33997          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
33998          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
33999          */
34000     reconfigure : function(dataSource, colModel){
34001         if(this.loadMask){
34002             this.loadMask.destroy();
34003             this.loadMask = new Roo.LoadMask(this.container,
34004                     Roo.apply({store:dataSource}, this.loadMask));
34005         }
34006         this.view.bind(dataSource, colModel);
34007         this.dataSource = dataSource;
34008         this.colModel = colModel;
34009         this.view.refresh(true);
34010     },
34011
34012     // private
34013     onKeyDown : function(e){
34014         this.fireEvent("keydown", e);
34015     },
34016
34017     /**
34018      * Destroy this grid.
34019      * @param {Boolean} removeEl True to remove the element
34020      */
34021     destroy : function(removeEl, keepListeners){
34022         if(this.loadMask){
34023             this.loadMask.destroy();
34024         }
34025         var c = this.container;
34026         c.removeAllListeners();
34027         this.view.destroy();
34028         this.colModel.purgeListeners();
34029         if(!keepListeners){
34030             this.purgeListeners();
34031         }
34032         c.update("");
34033         if(removeEl === true){
34034             c.remove();
34035         }
34036     },
34037
34038     // private
34039     processEvent : function(name, e){
34040         this.fireEvent(name, e);
34041         var t = e.getTarget();
34042         var v = this.view;
34043         var header = v.findHeaderIndex(t);
34044         if(header !== false){
34045             this.fireEvent("header" + name, this, header, e);
34046         }else{
34047             var row = v.findRowIndex(t);
34048             var cell = v.findCellIndex(t);
34049             if(row !== false){
34050                 this.fireEvent("row" + name, this, row, e);
34051                 if(cell !== false){
34052                     this.fireEvent("cell" + name, this, row, cell, e);
34053                 }
34054             }
34055         }
34056     },
34057
34058     // private
34059     onClick : function(e){
34060         this.processEvent("click", e);
34061     },
34062
34063     // private
34064     onContextMenu : function(e, t){
34065         this.processEvent("contextmenu", e);
34066     },
34067
34068     // private
34069     onDblClick : function(e){
34070         this.processEvent("dblclick", e);
34071     },
34072
34073     // private
34074     walkCells : function(row, col, step, fn, scope){
34075         var cm = this.colModel, clen = cm.getColumnCount();
34076         var ds = this.dataSource, rlen = ds.getCount(), first = true;
34077         if(step < 0){
34078             if(col < 0){
34079                 row--;
34080                 first = false;
34081             }
34082             while(row >= 0){
34083                 if(!first){
34084                     col = clen-1;
34085                 }
34086                 first = false;
34087                 while(col >= 0){
34088                     if(fn.call(scope || this, row, col, cm) === true){
34089                         return [row, col];
34090                     }
34091                     col--;
34092                 }
34093                 row--;
34094             }
34095         } else {
34096             if(col >= clen){
34097                 row++;
34098                 first = false;
34099             }
34100             while(row < rlen){
34101                 if(!first){
34102                     col = 0;
34103                 }
34104                 first = false;
34105                 while(col < clen){
34106                     if(fn.call(scope || this, row, col, cm) === true){
34107                         return [row, col];
34108                     }
34109                     col++;
34110                 }
34111                 row++;
34112             }
34113         }
34114         return null;
34115     },
34116
34117     // private
34118     getSelections : function(){
34119         return this.selModel.getSelections();
34120     },
34121
34122     /**
34123      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
34124      * but if manual update is required this method will initiate it.
34125      */
34126     autoSize : function(){
34127         if(this.rendered){
34128             this.view.layout();
34129             if(this.view.adjustForScroll){
34130                 this.view.adjustForScroll();
34131             }
34132         }
34133     },
34134
34135     /**
34136      * Returns the grid's underlying element.
34137      * @return {Element} The element
34138      */
34139     getGridEl : function(){
34140         return this.container;
34141     },
34142
34143     // private for compatibility, overridden by editor grid
34144     stopEditing : function(){},
34145
34146     /**
34147      * Returns the grid's SelectionModel.
34148      * @return {SelectionModel}
34149      */
34150     getSelectionModel : function(){
34151         if(!this.selModel){
34152             this.selModel = new Roo.grid.RowSelectionModel();
34153         }
34154         return this.selModel;
34155     },
34156
34157     /**
34158      * Returns the grid's DataSource.
34159      * @return {DataSource}
34160      */
34161     getDataSource : function(){
34162         return this.dataSource;
34163     },
34164
34165     /**
34166      * Returns the grid's ColumnModel.
34167      * @return {ColumnModel}
34168      */
34169     getColumnModel : function(){
34170         return this.colModel;
34171     },
34172
34173     /**
34174      * Returns the grid's GridView object.
34175      * @return {GridView}
34176      */
34177     getView : function(){
34178         if(!this.view){
34179             this.view = new Roo.grid.GridView(this.viewConfig);
34180         }
34181         return this.view;
34182     },
34183     /**
34184      * Called to get grid's drag proxy text, by default returns this.ddText.
34185      * @return {String}
34186      */
34187     getDragDropText : function(){
34188         var count = this.selModel.getCount();
34189         return String.format(this.ddText, count, count == 1 ? '' : 's');
34190     }
34191 });
34192 /**
34193  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
34194  * %0 is replaced with the number of selected rows.
34195  * @type String
34196  */
34197 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
34198  * Based on:
34199  * Ext JS Library 1.1.1
34200  * Copyright(c) 2006-2007, Ext JS, LLC.
34201  *
34202  * Originally Released Under LGPL - original licence link has changed is not relivant.
34203  *
34204  * Fork - LGPL
34205  * <script type="text/javascript">
34206  */
34207  
34208 Roo.grid.AbstractGridView = function(){
34209         this.grid = null;
34210         
34211         this.events = {
34212             "beforerowremoved" : true,
34213             "beforerowsinserted" : true,
34214             "beforerefresh" : true,
34215             "rowremoved" : true,
34216             "rowsinserted" : true,
34217             "rowupdated" : true,
34218             "refresh" : true
34219         };
34220     Roo.grid.AbstractGridView.superclass.constructor.call(this);
34221 };
34222
34223 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
34224     rowClass : "x-grid-row",
34225     cellClass : "x-grid-cell",
34226     tdClass : "x-grid-td",
34227     hdClass : "x-grid-hd",
34228     splitClass : "x-grid-hd-split",
34229     
34230         init: function(grid){
34231         this.grid = grid;
34232                 var cid = this.grid.getGridEl().id;
34233         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
34234         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
34235         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
34236         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
34237         },
34238         
34239         getColumnRenderers : function(){
34240         var renderers = [];
34241         var cm = this.grid.colModel;
34242         var colCount = cm.getColumnCount();
34243         for(var i = 0; i < colCount; i++){
34244             renderers[i] = cm.getRenderer(i);
34245         }
34246         return renderers;
34247     },
34248     
34249     getColumnIds : function(){
34250         var ids = [];
34251         var cm = this.grid.colModel;
34252         var colCount = cm.getColumnCount();
34253         for(var i = 0; i < colCount; i++){
34254             ids[i] = cm.getColumnId(i);
34255         }
34256         return ids;
34257     },
34258     
34259     getDataIndexes : function(){
34260         if(!this.indexMap){
34261             this.indexMap = this.buildIndexMap();
34262         }
34263         return this.indexMap.colToData;
34264     },
34265     
34266     getColumnIndexByDataIndex : function(dataIndex){
34267         if(!this.indexMap){
34268             this.indexMap = this.buildIndexMap();
34269         }
34270         return this.indexMap.dataToCol[dataIndex];
34271     },
34272     
34273     /**
34274      * Set a css style for a column dynamically. 
34275      * @param {Number} colIndex The index of the column
34276      * @param {String} name The css property name
34277      * @param {String} value The css value
34278      */
34279     setCSSStyle : function(colIndex, name, value){
34280         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
34281         Roo.util.CSS.updateRule(selector, name, value);
34282     },
34283     
34284     generateRules : function(cm){
34285         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
34286         Roo.util.CSS.removeStyleSheet(rulesId);
34287         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34288             var cid = cm.getColumnId(i);
34289             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
34290                          this.tdSelector, cid, " {\n}\n",
34291                          this.hdSelector, cid, " {\n}\n",
34292                          this.splitSelector, cid, " {\n}\n");
34293         }
34294         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34295     }
34296 });/*
34297  * Based on:
34298  * Ext JS Library 1.1.1
34299  * Copyright(c) 2006-2007, Ext JS, LLC.
34300  *
34301  * Originally Released Under LGPL - original licence link has changed is not relivant.
34302  *
34303  * Fork - LGPL
34304  * <script type="text/javascript">
34305  */
34306
34307 // private
34308 // This is a support class used internally by the Grid components
34309 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
34310     this.grid = grid;
34311     this.view = grid.getView();
34312     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34313     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
34314     if(hd2){
34315         this.setHandleElId(Roo.id(hd));
34316         this.setOuterHandleElId(Roo.id(hd2));
34317     }
34318     this.scroll = false;
34319 };
34320 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
34321     maxDragWidth: 120,
34322     getDragData : function(e){
34323         var t = Roo.lib.Event.getTarget(e);
34324         var h = this.view.findHeaderCell(t);
34325         if(h){
34326             return {ddel: h.firstChild, header:h};
34327         }
34328         return false;
34329     },
34330
34331     onInitDrag : function(e){
34332         this.view.headersDisabled = true;
34333         var clone = this.dragData.ddel.cloneNode(true);
34334         clone.id = Roo.id();
34335         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
34336         this.proxy.update(clone);
34337         return true;
34338     },
34339
34340     afterValidDrop : function(){
34341         var v = this.view;
34342         setTimeout(function(){
34343             v.headersDisabled = false;
34344         }, 50);
34345     },
34346
34347     afterInvalidDrop : function(){
34348         var v = this.view;
34349         setTimeout(function(){
34350             v.headersDisabled = false;
34351         }, 50);
34352     }
34353 });
34354 /*
34355  * Based on:
34356  * Ext JS Library 1.1.1
34357  * Copyright(c) 2006-2007, Ext JS, LLC.
34358  *
34359  * Originally Released Under LGPL - original licence link has changed is not relivant.
34360  *
34361  * Fork - LGPL
34362  * <script type="text/javascript">
34363  */
34364 // private
34365 // This is a support class used internally by the Grid components
34366 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
34367     this.grid = grid;
34368     this.view = grid.getView();
34369     // split the proxies so they don't interfere with mouse events
34370     this.proxyTop = Roo.DomHelper.append(document.body, {
34371         cls:"col-move-top", html:"&#160;"
34372     }, true);
34373     this.proxyBottom = Roo.DomHelper.append(document.body, {
34374         cls:"col-move-bottom", html:"&#160;"
34375     }, true);
34376     this.proxyTop.hide = this.proxyBottom.hide = function(){
34377         this.setLeftTop(-100,-100);
34378         this.setStyle("visibility", "hidden");
34379     };
34380     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34381     // temporarily disabled
34382     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
34383     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
34384 };
34385 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
34386     proxyOffsets : [-4, -9],
34387     fly: Roo.Element.fly,
34388
34389     getTargetFromEvent : function(e){
34390         var t = Roo.lib.Event.getTarget(e);
34391         var cindex = this.view.findCellIndex(t);
34392         if(cindex !== false){
34393             return this.view.getHeaderCell(cindex);
34394         }
34395         return null;
34396     },
34397
34398     nextVisible : function(h){
34399         var v = this.view, cm = this.grid.colModel;
34400         h = h.nextSibling;
34401         while(h){
34402             if(!cm.isHidden(v.getCellIndex(h))){
34403                 return h;
34404             }
34405             h = h.nextSibling;
34406         }
34407         return null;
34408     },
34409
34410     prevVisible : function(h){
34411         var v = this.view, cm = this.grid.colModel;
34412         h = h.prevSibling;
34413         while(h){
34414             if(!cm.isHidden(v.getCellIndex(h))){
34415                 return h;
34416             }
34417             h = h.prevSibling;
34418         }
34419         return null;
34420     },
34421
34422     positionIndicator : function(h, n, e){
34423         var x = Roo.lib.Event.getPageX(e);
34424         var r = Roo.lib.Dom.getRegion(n.firstChild);
34425         var px, pt, py = r.top + this.proxyOffsets[1];
34426         if((r.right - x) <= (r.right-r.left)/2){
34427             px = r.right+this.view.borderWidth;
34428             pt = "after";
34429         }else{
34430             px = r.left;
34431             pt = "before";
34432         }
34433         var oldIndex = this.view.getCellIndex(h);
34434         var newIndex = this.view.getCellIndex(n);
34435
34436         if(this.grid.colModel.isFixed(newIndex)){
34437             return false;
34438         }
34439
34440         var locked = this.grid.colModel.isLocked(newIndex);
34441
34442         if(pt == "after"){
34443             newIndex++;
34444         }
34445         if(oldIndex < newIndex){
34446             newIndex--;
34447         }
34448         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
34449             return false;
34450         }
34451         px +=  this.proxyOffsets[0];
34452         this.proxyTop.setLeftTop(px, py);
34453         this.proxyTop.show();
34454         if(!this.bottomOffset){
34455             this.bottomOffset = this.view.mainHd.getHeight();
34456         }
34457         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
34458         this.proxyBottom.show();
34459         return pt;
34460     },
34461
34462     onNodeEnter : function(n, dd, e, data){
34463         if(data.header != n){
34464             this.positionIndicator(data.header, n, e);
34465         }
34466     },
34467
34468     onNodeOver : function(n, dd, e, data){
34469         var result = false;
34470         if(data.header != n){
34471             result = this.positionIndicator(data.header, n, e);
34472         }
34473         if(!result){
34474             this.proxyTop.hide();
34475             this.proxyBottom.hide();
34476         }
34477         return result ? this.dropAllowed : this.dropNotAllowed;
34478     },
34479
34480     onNodeOut : function(n, dd, e, data){
34481         this.proxyTop.hide();
34482         this.proxyBottom.hide();
34483     },
34484
34485     onNodeDrop : function(n, dd, e, data){
34486         var h = data.header;
34487         if(h != n){
34488             var cm = this.grid.colModel;
34489             var x = Roo.lib.Event.getPageX(e);
34490             var r = Roo.lib.Dom.getRegion(n.firstChild);
34491             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
34492             var oldIndex = this.view.getCellIndex(h);
34493             var newIndex = this.view.getCellIndex(n);
34494             var locked = cm.isLocked(newIndex);
34495             if(pt == "after"){
34496                 newIndex++;
34497             }
34498             if(oldIndex < newIndex){
34499                 newIndex--;
34500             }
34501             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
34502                 return false;
34503             }
34504             cm.setLocked(oldIndex, locked, true);
34505             cm.moveColumn(oldIndex, newIndex);
34506             this.grid.fireEvent("columnmove", oldIndex, newIndex);
34507             return true;
34508         }
34509         return false;
34510     }
34511 });
34512 /*
34513  * Based on:
34514  * Ext JS Library 1.1.1
34515  * Copyright(c) 2006-2007, Ext JS, LLC.
34516  *
34517  * Originally Released Under LGPL - original licence link has changed is not relivant.
34518  *
34519  * Fork - LGPL
34520  * <script type="text/javascript">
34521  */
34522   
34523 /**
34524  * @class Roo.grid.GridView
34525  * @extends Roo.util.Observable
34526  *
34527  * @constructor
34528  * @param {Object} config
34529  */
34530 Roo.grid.GridView = function(config){
34531     Roo.grid.GridView.superclass.constructor.call(this);
34532     this.el = null;
34533
34534     Roo.apply(this, config);
34535 };
34536
34537 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
34538
34539     
34540     rowClass : "x-grid-row",
34541
34542     cellClass : "x-grid-col",
34543
34544     tdClass : "x-grid-td",
34545
34546     hdClass : "x-grid-hd",
34547
34548     splitClass : "x-grid-split",
34549
34550     sortClasses : ["sort-asc", "sort-desc"],
34551
34552     enableMoveAnim : false,
34553
34554     hlColor: "C3DAF9",
34555
34556     dh : Roo.DomHelper,
34557
34558     fly : Roo.Element.fly,
34559
34560     css : Roo.util.CSS,
34561
34562     borderWidth: 1,
34563
34564     splitOffset: 3,
34565
34566     scrollIncrement : 22,
34567
34568     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
34569
34570     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
34571
34572     bind : function(ds, cm){
34573         if(this.ds){
34574             this.ds.un("load", this.onLoad, this);
34575             this.ds.un("datachanged", this.onDataChange, this);
34576             this.ds.un("add", this.onAdd, this);
34577             this.ds.un("remove", this.onRemove, this);
34578             this.ds.un("update", this.onUpdate, this);
34579             this.ds.un("clear", this.onClear, this);
34580         }
34581         if(ds){
34582             ds.on("load", this.onLoad, this);
34583             ds.on("datachanged", this.onDataChange, this);
34584             ds.on("add", this.onAdd, this);
34585             ds.on("remove", this.onRemove, this);
34586             ds.on("update", this.onUpdate, this);
34587             ds.on("clear", this.onClear, this);
34588         }
34589         this.ds = ds;
34590
34591         if(this.cm){
34592             this.cm.un("widthchange", this.onColWidthChange, this);
34593             this.cm.un("headerchange", this.onHeaderChange, this);
34594             this.cm.un("hiddenchange", this.onHiddenChange, this);
34595             this.cm.un("columnmoved", this.onColumnMove, this);
34596             this.cm.un("columnlockchange", this.onColumnLock, this);
34597         }
34598         if(cm){
34599             this.generateRules(cm);
34600             cm.on("widthchange", this.onColWidthChange, this);
34601             cm.on("headerchange", this.onHeaderChange, this);
34602             cm.on("hiddenchange", this.onHiddenChange, this);
34603             cm.on("columnmoved", this.onColumnMove, this);
34604             cm.on("columnlockchange", this.onColumnLock, this);
34605         }
34606         this.cm = cm;
34607     },
34608
34609     init: function(grid){
34610         Roo.grid.GridView.superclass.init.call(this, grid);
34611
34612         this.bind(grid.dataSource, grid.colModel);
34613
34614         grid.on("headerclick", this.handleHeaderClick, this);
34615
34616         if(grid.trackMouseOver){
34617             grid.on("mouseover", this.onRowOver, this);
34618             grid.on("mouseout", this.onRowOut, this);
34619         }
34620         grid.cancelTextSelection = function(){};
34621         this.gridId = grid.id;
34622
34623         var tpls = this.templates || {};
34624
34625         if(!tpls.master){
34626             tpls.master = new Roo.Template(
34627                '<div class="x-grid" hidefocus="true">',
34628                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
34629                   '<div class="x-grid-topbar"></div>',
34630                   '<div class="x-grid-scroller"><div></div></div>',
34631                   '<div class="x-grid-locked">',
34632                       '<div class="x-grid-header">{lockedHeader}</div>',
34633                       '<div class="x-grid-body">{lockedBody}</div>',
34634                   "</div>",
34635                   '<div class="x-grid-viewport">',
34636                       '<div class="x-grid-header">{header}</div>',
34637                       '<div class="x-grid-body">{body}</div>',
34638                   "</div>",
34639                   '<div class="x-grid-bottombar"></div>',
34640                  
34641                   '<div class="x-grid-resize-proxy">&#160;</div>',
34642                "</div>"
34643             );
34644             tpls.master.disableformats = true;
34645         }
34646
34647         if(!tpls.header){
34648             tpls.header = new Roo.Template(
34649                '<table border="0" cellspacing="0" cellpadding="0">',
34650                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
34651                "</table>{splits}"
34652             );
34653             tpls.header.disableformats = true;
34654         }
34655         tpls.header.compile();
34656
34657         if(!tpls.hcell){
34658             tpls.hcell = new Roo.Template(
34659                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
34660                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
34661                 "</div></td>"
34662              );
34663              tpls.hcell.disableFormats = true;
34664         }
34665         tpls.hcell.compile();
34666
34667         if(!tpls.hsplit){
34668             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
34669             tpls.hsplit.disableFormats = true;
34670         }
34671         tpls.hsplit.compile();
34672
34673         if(!tpls.body){
34674             tpls.body = new Roo.Template(
34675                '<table border="0" cellspacing="0" cellpadding="0">',
34676                "<tbody>{rows}</tbody>",
34677                "</table>"
34678             );
34679             tpls.body.disableFormats = true;
34680         }
34681         tpls.body.compile();
34682
34683         if(!tpls.row){
34684             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
34685             tpls.row.disableFormats = true;
34686         }
34687         tpls.row.compile();
34688
34689         if(!tpls.cell){
34690             tpls.cell = new Roo.Template(
34691                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
34692                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
34693                 "</td>"
34694             );
34695             tpls.cell.disableFormats = true;
34696         }
34697         tpls.cell.compile();
34698
34699         this.templates = tpls;
34700     },
34701
34702     // remap these for backwards compat
34703     onColWidthChange : function(){
34704         this.updateColumns.apply(this, arguments);
34705     },
34706     onHeaderChange : function(){
34707         this.updateHeaders.apply(this, arguments);
34708     }, 
34709     onHiddenChange : function(){
34710         this.handleHiddenChange.apply(this, arguments);
34711     },
34712     onColumnMove : function(){
34713         this.handleColumnMove.apply(this, arguments);
34714     },
34715     onColumnLock : function(){
34716         this.handleLockChange.apply(this, arguments);
34717     },
34718
34719     onDataChange : function(){
34720         this.refresh();
34721         this.updateHeaderSortState();
34722     },
34723
34724     onClear : function(){
34725         this.refresh();
34726     },
34727
34728     onUpdate : function(ds, record){
34729         this.refreshRow(record);
34730     },
34731
34732     refreshRow : function(record){
34733         var ds = this.ds, index;
34734         if(typeof record == 'number'){
34735             index = record;
34736             record = ds.getAt(index);
34737         }else{
34738             index = ds.indexOf(record);
34739         }
34740         this.insertRows(ds, index, index, true);
34741         this.onRemove(ds, record, index+1, true);
34742         this.syncRowHeights(index, index);
34743         this.layout();
34744         this.fireEvent("rowupdated", this, index, record);
34745     },
34746
34747     onAdd : function(ds, records, index){
34748         this.insertRows(ds, index, index + (records.length-1));
34749     },
34750
34751     onRemove : function(ds, record, index, isUpdate){
34752         if(isUpdate !== true){
34753             this.fireEvent("beforerowremoved", this, index, record);
34754         }
34755         var bt = this.getBodyTable(), lt = this.getLockedTable();
34756         if(bt.rows[index]){
34757             bt.firstChild.removeChild(bt.rows[index]);
34758         }
34759         if(lt.rows[index]){
34760             lt.firstChild.removeChild(lt.rows[index]);
34761         }
34762         if(isUpdate !== true){
34763             this.stripeRows(index);
34764             this.syncRowHeights(index, index);
34765             this.layout();
34766             this.fireEvent("rowremoved", this, index, record);
34767         }
34768     },
34769
34770     onLoad : function(){
34771         this.scrollToTop();
34772     },
34773
34774     /**
34775      * Scrolls the grid to the top
34776      */
34777     scrollToTop : function(){
34778         if(this.scroller){
34779             this.scroller.dom.scrollTop = 0;
34780             this.syncScroll();
34781         }
34782     },
34783
34784     /**
34785      * Gets a panel in the header of the grid that can be used for toolbars etc.
34786      * After modifying the contents of this panel a call to grid.autoSize() may be
34787      * required to register any changes in size.
34788      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
34789      * @return Roo.Element
34790      */
34791     getHeaderPanel : function(doShow){
34792         if(doShow){
34793             this.headerPanel.show();
34794         }
34795         return this.headerPanel;
34796     },
34797
34798     /**
34799      * Gets a panel in the footer of the grid that can be used for toolbars etc.
34800      * After modifying the contents of this panel a call to grid.autoSize() may be
34801      * required to register any changes in size.
34802      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
34803      * @return Roo.Element
34804      */
34805     getFooterPanel : function(doShow){
34806         if(doShow){
34807             this.footerPanel.show();
34808         }
34809         return this.footerPanel;
34810     },
34811
34812     initElements : function(){
34813         var E = Roo.Element;
34814         var el = this.grid.getGridEl().dom.firstChild;
34815         var cs = el.childNodes;
34816
34817         this.el = new E(el);
34818         
34819          this.focusEl = new E(el.firstChild);
34820         this.focusEl.swallowEvent("click", true);
34821         
34822         this.headerPanel = new E(cs[1]);
34823         this.headerPanel.enableDisplayMode("block");
34824
34825         this.scroller = new E(cs[2]);
34826         this.scrollSizer = new E(this.scroller.dom.firstChild);
34827
34828         this.lockedWrap = new E(cs[3]);
34829         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
34830         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
34831
34832         this.mainWrap = new E(cs[4]);
34833         this.mainHd = new E(this.mainWrap.dom.firstChild);
34834         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
34835
34836         this.footerPanel = new E(cs[5]);
34837         this.footerPanel.enableDisplayMode("block");
34838
34839         this.resizeProxy = new E(cs[6]);
34840
34841         this.headerSelector = String.format(
34842            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
34843            this.lockedHd.id, this.mainHd.id
34844         );
34845
34846         this.splitterSelector = String.format(
34847            '#{0} div.x-grid-split, #{1} div.x-grid-split',
34848            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
34849         );
34850     },
34851     idToCssName : function(s)
34852     {
34853         return s.replace(/[^a-z0-9]+/ig, '-');
34854     },
34855
34856     getHeaderCell : function(index){
34857         return Roo.DomQuery.select(this.headerSelector)[index];
34858     },
34859
34860     getHeaderCellMeasure : function(index){
34861         return this.getHeaderCell(index).firstChild;
34862     },
34863
34864     getHeaderCellText : function(index){
34865         return this.getHeaderCell(index).firstChild.firstChild;
34866     },
34867
34868     getLockedTable : function(){
34869         return this.lockedBody.dom.firstChild;
34870     },
34871
34872     getBodyTable : function(){
34873         return this.mainBody.dom.firstChild;
34874     },
34875
34876     getLockedRow : function(index){
34877         return this.getLockedTable().rows[index];
34878     },
34879
34880     getRow : function(index){
34881         return this.getBodyTable().rows[index];
34882     },
34883
34884     getRowComposite : function(index){
34885         if(!this.rowEl){
34886             this.rowEl = new Roo.CompositeElementLite();
34887         }
34888         var els = [], lrow, mrow;
34889         if(lrow = this.getLockedRow(index)){
34890             els.push(lrow);
34891         }
34892         if(mrow = this.getRow(index)){
34893             els.push(mrow);
34894         }
34895         this.rowEl.elements = els;
34896         return this.rowEl;
34897     },
34898     /**
34899      * Gets the 'td' of the cell
34900      * 
34901      * @param {Integer} rowIndex row to select
34902      * @param {Integer} colIndex column to select
34903      * 
34904      * @return {Object} 
34905      */
34906     getCell : function(rowIndex, colIndex){
34907         var locked = this.cm.getLockedCount();
34908         var source;
34909         if(colIndex < locked){
34910             source = this.lockedBody.dom.firstChild;
34911         }else{
34912             source = this.mainBody.dom.firstChild;
34913             colIndex -= locked;
34914         }
34915         return source.rows[rowIndex].childNodes[colIndex];
34916     },
34917
34918     getCellText : function(rowIndex, colIndex){
34919         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
34920     },
34921
34922     getCellBox : function(cell){
34923         var b = this.fly(cell).getBox();
34924         if(Roo.isOpera){ // opera fails to report the Y
34925             b.y = cell.offsetTop + this.mainBody.getY();
34926         }
34927         return b;
34928     },
34929
34930     getCellIndex : function(cell){
34931         var id = String(cell.className).match(this.cellRE);
34932         if(id){
34933             return parseInt(id[1], 10);
34934         }
34935         return 0;
34936     },
34937
34938     findHeaderIndex : function(n){
34939         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
34940         return r ? this.getCellIndex(r) : false;
34941     },
34942
34943     findHeaderCell : function(n){
34944         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
34945         return r ? r : false;
34946     },
34947
34948     findRowIndex : function(n){
34949         if(!n){
34950             return false;
34951         }
34952         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
34953         return r ? r.rowIndex : false;
34954     },
34955
34956     findCellIndex : function(node){
34957         var stop = this.el.dom;
34958         while(node && node != stop){
34959             if(this.findRE.test(node.className)){
34960                 return this.getCellIndex(node);
34961             }
34962             node = node.parentNode;
34963         }
34964         return false;
34965     },
34966
34967     getColumnId : function(index){
34968         return this.cm.getColumnId(index);
34969     },
34970
34971     getSplitters : function()
34972     {
34973         if(this.splitterSelector){
34974            return Roo.DomQuery.select(this.splitterSelector);
34975         }else{
34976             return null;
34977       }
34978     },
34979
34980     getSplitter : function(index){
34981         return this.getSplitters()[index];
34982     },
34983
34984     onRowOver : function(e, t){
34985         var row;
34986         if((row = this.findRowIndex(t)) !== false){
34987             this.getRowComposite(row).addClass("x-grid-row-over");
34988         }
34989     },
34990
34991     onRowOut : function(e, t){
34992         var row;
34993         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
34994             this.getRowComposite(row).removeClass("x-grid-row-over");
34995         }
34996     },
34997
34998     renderHeaders : function(){
34999         var cm = this.cm;
35000         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
35001         var cb = [], lb = [], sb = [], lsb = [], p = {};
35002         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35003             p.cellId = "x-grid-hd-0-" + i;
35004             p.splitId = "x-grid-csplit-0-" + i;
35005             p.id = cm.getColumnId(i);
35006             p.title = cm.getColumnTooltip(i) || "";
35007             p.value = cm.getColumnHeader(i) || "";
35008             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
35009             if(!cm.isLocked(i)){
35010                 cb[cb.length] = ct.apply(p);
35011                 sb[sb.length] = st.apply(p);
35012             }else{
35013                 lb[lb.length] = ct.apply(p);
35014                 lsb[lsb.length] = st.apply(p);
35015             }
35016         }
35017         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
35018                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
35019     },
35020
35021     updateHeaders : function(){
35022         var html = this.renderHeaders();
35023         this.lockedHd.update(html[0]);
35024         this.mainHd.update(html[1]);
35025     },
35026
35027     /**
35028      * Focuses the specified row.
35029      * @param {Number} row The row index
35030      */
35031     focusRow : function(row)
35032     {
35033         //Roo.log('GridView.focusRow');
35034         var x = this.scroller.dom.scrollLeft;
35035         this.focusCell(row, 0, false);
35036         this.scroller.dom.scrollLeft = x;
35037     },
35038
35039     /**
35040      * Focuses the specified cell.
35041      * @param {Number} row The row index
35042      * @param {Number} col The column index
35043      * @param {Boolean} hscroll false to disable horizontal scrolling
35044      */
35045     focusCell : function(row, col, hscroll)
35046     {
35047         //Roo.log('GridView.focusCell');
35048         var el = this.ensureVisible(row, col, hscroll);
35049         this.focusEl.alignTo(el, "tl-tl");
35050         if(Roo.isGecko){
35051             this.focusEl.focus();
35052         }else{
35053             this.focusEl.focus.defer(1, this.focusEl);
35054         }
35055     },
35056
35057     /**
35058      * Scrolls the specified cell into view
35059      * @param {Number} row The row index
35060      * @param {Number} col The column index
35061      * @param {Boolean} hscroll false to disable horizontal scrolling
35062      */
35063     ensureVisible : function(row, col, hscroll)
35064     {
35065         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
35066         //return null; //disable for testing.
35067         if(typeof row != "number"){
35068             row = row.rowIndex;
35069         }
35070         if(row < 0 && row >= this.ds.getCount()){
35071             return  null;
35072         }
35073         col = (col !== undefined ? col : 0);
35074         var cm = this.grid.colModel;
35075         while(cm.isHidden(col)){
35076             col++;
35077         }
35078
35079         var el = this.getCell(row, col);
35080         if(!el){
35081             return null;
35082         }
35083         var c = this.scroller.dom;
35084
35085         var ctop = parseInt(el.offsetTop, 10);
35086         var cleft = parseInt(el.offsetLeft, 10);
35087         var cbot = ctop + el.offsetHeight;
35088         var cright = cleft + el.offsetWidth;
35089         
35090         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
35091         var stop = parseInt(c.scrollTop, 10);
35092         var sleft = parseInt(c.scrollLeft, 10);
35093         var sbot = stop + ch;
35094         var sright = sleft + c.clientWidth;
35095         /*
35096         Roo.log('GridView.ensureVisible:' +
35097                 ' ctop:' + ctop +
35098                 ' c.clientHeight:' + c.clientHeight +
35099                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
35100                 ' stop:' + stop +
35101                 ' cbot:' + cbot +
35102                 ' sbot:' + sbot +
35103                 ' ch:' + ch  
35104                 );
35105         */
35106         if(ctop < stop){
35107              c.scrollTop = ctop;
35108             //Roo.log("set scrolltop to ctop DISABLE?");
35109         }else if(cbot > sbot){
35110             //Roo.log("set scrolltop to cbot-ch");
35111             c.scrollTop = cbot-ch;
35112         }
35113         
35114         if(hscroll !== false){
35115             if(cleft < sleft){
35116                 c.scrollLeft = cleft;
35117             }else if(cright > sright){
35118                 c.scrollLeft = cright-c.clientWidth;
35119             }
35120         }
35121          
35122         return el;
35123     },
35124
35125     updateColumns : function(){
35126         this.grid.stopEditing();
35127         var cm = this.grid.colModel, colIds = this.getColumnIds();
35128         //var totalWidth = cm.getTotalWidth();
35129         var pos = 0;
35130         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35131             //if(cm.isHidden(i)) continue;
35132             var w = cm.getColumnWidth(i);
35133             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35134             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35135         }
35136         this.updateSplitters();
35137     },
35138
35139     generateRules : function(cm){
35140         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
35141         Roo.util.CSS.removeStyleSheet(rulesId);
35142         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35143             var cid = cm.getColumnId(i);
35144             var align = '';
35145             if(cm.config[i].align){
35146                 align = 'text-align:'+cm.config[i].align+';';
35147             }
35148             var hidden = '';
35149             if(cm.isHidden(i)){
35150                 hidden = 'display:none;';
35151             }
35152             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
35153             ruleBuf.push(
35154                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
35155                     this.hdSelector, cid, " {\n", align, width, "}\n",
35156                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
35157                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
35158         }
35159         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
35160     },
35161
35162     updateSplitters : function(){
35163         var cm = this.cm, s = this.getSplitters();
35164         if(s){ // splitters not created yet
35165             var pos = 0, locked = true;
35166             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35167                 if(cm.isHidden(i)) continue;
35168                 var w = cm.getColumnWidth(i); // make sure it's a number
35169                 if(!cm.isLocked(i) && locked){
35170                     pos = 0;
35171                     locked = false;
35172                 }
35173                 pos += w;
35174                 s[i].style.left = (pos-this.splitOffset) + "px";
35175             }
35176         }
35177     },
35178
35179     handleHiddenChange : function(colModel, colIndex, hidden){
35180         if(hidden){
35181             this.hideColumn(colIndex);
35182         }else{
35183             this.unhideColumn(colIndex);
35184         }
35185     },
35186
35187     hideColumn : function(colIndex){
35188         var cid = this.getColumnId(colIndex);
35189         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
35190         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
35191         if(Roo.isSafari){
35192             this.updateHeaders();
35193         }
35194         this.updateSplitters();
35195         this.layout();
35196     },
35197
35198     unhideColumn : function(colIndex){
35199         var cid = this.getColumnId(colIndex);
35200         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
35201         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
35202
35203         if(Roo.isSafari){
35204             this.updateHeaders();
35205         }
35206         this.updateSplitters();
35207         this.layout();
35208     },
35209
35210     insertRows : function(dm, firstRow, lastRow, isUpdate){
35211         if(firstRow == 0 && lastRow == dm.getCount()-1){
35212             this.refresh();
35213         }else{
35214             if(!isUpdate){
35215                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
35216             }
35217             var s = this.getScrollState();
35218             var markup = this.renderRows(firstRow, lastRow);
35219             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
35220             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
35221             this.restoreScroll(s);
35222             if(!isUpdate){
35223                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
35224                 this.syncRowHeights(firstRow, lastRow);
35225                 this.stripeRows(firstRow);
35226                 this.layout();
35227             }
35228         }
35229     },
35230
35231     bufferRows : function(markup, target, index){
35232         var before = null, trows = target.rows, tbody = target.tBodies[0];
35233         if(index < trows.length){
35234             before = trows[index];
35235         }
35236         var b = document.createElement("div");
35237         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
35238         var rows = b.firstChild.rows;
35239         for(var i = 0, len = rows.length; i < len; i++){
35240             if(before){
35241                 tbody.insertBefore(rows[0], before);
35242             }else{
35243                 tbody.appendChild(rows[0]);
35244             }
35245         }
35246         b.innerHTML = "";
35247         b = null;
35248     },
35249
35250     deleteRows : function(dm, firstRow, lastRow){
35251         if(dm.getRowCount()<1){
35252             this.fireEvent("beforerefresh", this);
35253             this.mainBody.update("");
35254             this.lockedBody.update("");
35255             this.fireEvent("refresh", this);
35256         }else{
35257             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
35258             var bt = this.getBodyTable();
35259             var tbody = bt.firstChild;
35260             var rows = bt.rows;
35261             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
35262                 tbody.removeChild(rows[firstRow]);
35263             }
35264             this.stripeRows(firstRow);
35265             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
35266         }
35267     },
35268
35269     updateRows : function(dataSource, firstRow, lastRow){
35270         var s = this.getScrollState();
35271         this.refresh();
35272         this.restoreScroll(s);
35273     },
35274
35275     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
35276         if(!noRefresh){
35277            this.refresh();
35278         }
35279         this.updateHeaderSortState();
35280     },
35281
35282     getScrollState : function(){
35283         
35284         var sb = this.scroller.dom;
35285         return {left: sb.scrollLeft, top: sb.scrollTop};
35286     },
35287
35288     stripeRows : function(startRow){
35289         if(!this.grid.stripeRows || this.ds.getCount() < 1){
35290             return;
35291         }
35292         startRow = startRow || 0;
35293         var rows = this.getBodyTable().rows;
35294         var lrows = this.getLockedTable().rows;
35295         var cls = ' x-grid-row-alt ';
35296         for(var i = startRow, len = rows.length; i < len; i++){
35297             var row = rows[i], lrow = lrows[i];
35298             var isAlt = ((i+1) % 2 == 0);
35299             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
35300             if(isAlt == hasAlt){
35301                 continue;
35302             }
35303             if(isAlt){
35304                 row.className += " x-grid-row-alt";
35305             }else{
35306                 row.className = row.className.replace("x-grid-row-alt", "");
35307             }
35308             if(lrow){
35309                 lrow.className = row.className;
35310             }
35311         }
35312     },
35313
35314     restoreScroll : function(state){
35315         //Roo.log('GridView.restoreScroll');
35316         var sb = this.scroller.dom;
35317         sb.scrollLeft = state.left;
35318         sb.scrollTop = state.top;
35319         this.syncScroll();
35320     },
35321
35322     syncScroll : function(){
35323         //Roo.log('GridView.syncScroll');
35324         var sb = this.scroller.dom;
35325         var sh = this.mainHd.dom;
35326         var bs = this.mainBody.dom;
35327         var lv = this.lockedBody.dom;
35328         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
35329         lv.scrollTop = bs.scrollTop = sb.scrollTop;
35330     },
35331
35332     handleScroll : function(e){
35333         this.syncScroll();
35334         var sb = this.scroller.dom;
35335         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
35336         e.stopEvent();
35337     },
35338
35339     handleWheel : function(e){
35340         var d = e.getWheelDelta();
35341         this.scroller.dom.scrollTop -= d*22;
35342         // set this here to prevent jumpy scrolling on large tables
35343         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
35344         e.stopEvent();
35345     },
35346
35347     renderRows : function(startRow, endRow){
35348         // pull in all the crap needed to render rows
35349         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
35350         var colCount = cm.getColumnCount();
35351
35352         if(ds.getCount() < 1){
35353             return ["", ""];
35354         }
35355
35356         // build a map for all the columns
35357         var cs = [];
35358         for(var i = 0; i < colCount; i++){
35359             var name = cm.getDataIndex(i);
35360             cs[i] = {
35361                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
35362                 renderer : cm.getRenderer(i),
35363                 id : cm.getColumnId(i),
35364                 locked : cm.isLocked(i)
35365             };
35366         }
35367
35368         startRow = startRow || 0;
35369         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
35370
35371         // records to render
35372         var rs = ds.getRange(startRow, endRow);
35373
35374         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
35375     },
35376
35377     // As much as I hate to duplicate code, this was branched because FireFox really hates
35378     // [].join("") on strings. The performance difference was substantial enough to
35379     // branch this function
35380     doRender : Roo.isGecko ?
35381             function(cs, rs, ds, startRow, colCount, stripe){
35382                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35383                 // buffers
35384                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35385                 
35386                 var hasListener = this.grid.hasListener('rowclass');
35387                 var rowcfg = {};
35388                 for(var j = 0, len = rs.length; j < len; j++){
35389                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
35390                     for(var i = 0; i < colCount; i++){
35391                         c = cs[i];
35392                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35393                         p.id = c.id;
35394                         p.css = p.attr = "";
35395                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35396                         if(p.value == undefined || p.value === "") p.value = "&#160;";
35397                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35398                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
35399                         }
35400                         var markup = ct.apply(p);
35401                         if(!c.locked){
35402                             cb+= markup;
35403                         }else{
35404                             lcb+= markup;
35405                         }
35406                     }
35407                     var alt = [];
35408                     if(stripe && ((rowIndex+1) % 2 == 0)){
35409                         alt.push("x-grid-row-alt")
35410                     }
35411                     if(r.dirty){
35412                         alt.push(  " x-grid-dirty-row");
35413                     }
35414                     rp.cells = lcb;
35415                     if(this.getRowClass){
35416                         alt.push(this.getRowClass(r, rowIndex));
35417                     }
35418                     if (hasListener) {
35419                         rowcfg = {
35420                              
35421                             record: r,
35422                             rowIndex : rowIndex,
35423                             rowClass : ''
35424                         }
35425                         this.grid.fireEvent('rowclass', this, rowcfg);
35426                         alt.push(rowcfg.rowClass);
35427                     }
35428                     rp.alt = alt.join(" ");
35429                     lbuf+= rt.apply(rp);
35430                     rp.cells = cb;
35431                     buf+=  rt.apply(rp);
35432                 }
35433                 return [lbuf, buf];
35434             } :
35435             function(cs, rs, ds, startRow, colCount, stripe){
35436                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35437                 // buffers
35438                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35439                 var hasListener = this.grid.hasListener('rowclass');
35440  
35441                 var rowcfg = {};
35442                 for(var j = 0, len = rs.length; j < len; j++){
35443                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
35444                     for(var i = 0; i < colCount; i++){
35445                         c = cs[i];
35446                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35447                         p.id = c.id;
35448                         p.css = p.attr = "";
35449                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35450                         if(p.value == undefined || p.value === "") p.value = "&#160;";
35451                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35452                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
35453                         }
35454                         
35455                         var markup = ct.apply(p);
35456                         if(!c.locked){
35457                             cb[cb.length] = markup;
35458                         }else{
35459                             lcb[lcb.length] = markup;
35460                         }
35461                     }
35462                     var alt = [];
35463                     if(stripe && ((rowIndex+1) % 2 == 0)){
35464                         alt.push( "x-grid-row-alt");
35465                     }
35466                     if(r.dirty){
35467                         alt.push(" x-grid-dirty-row");
35468                     }
35469                     rp.cells = lcb;
35470                     if(this.getRowClass){
35471                         alt.push( this.getRowClass(r, rowIndex));
35472                     }
35473                     if (hasListener) {
35474                         rowcfg = {
35475                              
35476                             record: r,
35477                             rowIndex : rowIndex,
35478                             rowClass : ''
35479                         }
35480                         this.grid.fireEvent('rowclass', this, rowcfg);
35481                         alt.push(rowcfg.rowClass);
35482                     }
35483                     rp.alt = alt.join(" ");
35484                     rp.cells = lcb.join("");
35485                     lbuf[lbuf.length] = rt.apply(rp);
35486                     rp.cells = cb.join("");
35487                     buf[buf.length] =  rt.apply(rp);
35488                 }
35489                 return [lbuf.join(""), buf.join("")];
35490             },
35491
35492     renderBody : function(){
35493         var markup = this.renderRows();
35494         var bt = this.templates.body;
35495         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
35496     },
35497
35498     /**
35499      * Refreshes the grid
35500      * @param {Boolean} headersToo
35501      */
35502     refresh : function(headersToo){
35503         this.fireEvent("beforerefresh", this);
35504         this.grid.stopEditing();
35505         var result = this.renderBody();
35506         this.lockedBody.update(result[0]);
35507         this.mainBody.update(result[1]);
35508         if(headersToo === true){
35509             this.updateHeaders();
35510             this.updateColumns();
35511             this.updateSplitters();
35512             this.updateHeaderSortState();
35513         }
35514         this.syncRowHeights();
35515         this.layout();
35516         this.fireEvent("refresh", this);
35517     },
35518
35519     handleColumnMove : function(cm, oldIndex, newIndex){
35520         this.indexMap = null;
35521         var s = this.getScrollState();
35522         this.refresh(true);
35523         this.restoreScroll(s);
35524         this.afterMove(newIndex);
35525     },
35526
35527     afterMove : function(colIndex){
35528         if(this.enableMoveAnim && Roo.enableFx){
35529             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
35530         }
35531         // if multisort - fix sortOrder, and reload..
35532         if (this.grid.dataSource.multiSort) {
35533             // the we can call sort again..
35534             var dm = this.grid.dataSource;
35535             var cm = this.grid.colModel;
35536             var so = [];
35537             for(var i = 0; i < cm.config.length; i++ ) {
35538                 
35539                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
35540                     continue; // dont' bother, it's not in sort list or being set.
35541                 }
35542                 
35543                 so.push(cm.config[i].dataIndex);
35544             };
35545             dm.sortOrder = so;
35546             dm.load(dm.lastOptions);
35547             
35548             
35549         }
35550         
35551     },
35552
35553     updateCell : function(dm, rowIndex, dataIndex){
35554         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
35555         if(typeof colIndex == "undefined"){ // not present in grid
35556             return;
35557         }
35558         var cm = this.grid.colModel;
35559         var cell = this.getCell(rowIndex, colIndex);
35560         var cellText = this.getCellText(rowIndex, colIndex);
35561
35562         var p = {
35563             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
35564             id : cm.getColumnId(colIndex),
35565             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
35566         };
35567         var renderer = cm.getRenderer(colIndex);
35568         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
35569         if(typeof val == "undefined" || val === "") val = "&#160;";
35570         cellText.innerHTML = val;
35571         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
35572         this.syncRowHeights(rowIndex, rowIndex);
35573     },
35574
35575     calcColumnWidth : function(colIndex, maxRowsToMeasure){
35576         var maxWidth = 0;
35577         if(this.grid.autoSizeHeaders){
35578             var h = this.getHeaderCellMeasure(colIndex);
35579             maxWidth = Math.max(maxWidth, h.scrollWidth);
35580         }
35581         var tb, index;
35582         if(this.cm.isLocked(colIndex)){
35583             tb = this.getLockedTable();
35584             index = colIndex;
35585         }else{
35586             tb = this.getBodyTable();
35587             index = colIndex - this.cm.getLockedCount();
35588         }
35589         if(tb && tb.rows){
35590             var rows = tb.rows;
35591             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
35592             for(var i = 0; i < stopIndex; i++){
35593                 var cell = rows[i].childNodes[index].firstChild;
35594                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
35595             }
35596         }
35597         return maxWidth + /*margin for error in IE*/ 5;
35598     },
35599     /**
35600      * Autofit a column to its content.
35601      * @param {Number} colIndex
35602      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
35603      */
35604      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
35605          if(this.cm.isHidden(colIndex)){
35606              return; // can't calc a hidden column
35607          }
35608         if(forceMinSize){
35609             var cid = this.cm.getColumnId(colIndex);
35610             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
35611            if(this.grid.autoSizeHeaders){
35612                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
35613            }
35614         }
35615         var newWidth = this.calcColumnWidth(colIndex);
35616         this.cm.setColumnWidth(colIndex,
35617             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
35618         if(!suppressEvent){
35619             this.grid.fireEvent("columnresize", colIndex, newWidth);
35620         }
35621     },
35622
35623     /**
35624      * Autofits all columns to their content and then expands to fit any extra space in the grid
35625      */
35626      autoSizeColumns : function(){
35627         var cm = this.grid.colModel;
35628         var colCount = cm.getColumnCount();
35629         for(var i = 0; i < colCount; i++){
35630             this.autoSizeColumn(i, true, true);
35631         }
35632         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
35633             this.fitColumns();
35634         }else{
35635             this.updateColumns();
35636             this.layout();
35637         }
35638     },
35639
35640     /**
35641      * Autofits all columns to the grid's width proportionate with their current size
35642      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
35643      */
35644     fitColumns : function(reserveScrollSpace){
35645         var cm = this.grid.colModel;
35646         var colCount = cm.getColumnCount();
35647         var cols = [];
35648         var width = 0;
35649         var i, w;
35650         for (i = 0; i < colCount; i++){
35651             if(!cm.isHidden(i) && !cm.isFixed(i)){
35652                 w = cm.getColumnWidth(i);
35653                 cols.push(i);
35654                 cols.push(w);
35655                 width += w;
35656             }
35657         }
35658         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
35659         if(reserveScrollSpace){
35660             avail -= 17;
35661         }
35662         var frac = (avail - cm.getTotalWidth())/width;
35663         while (cols.length){
35664             w = cols.pop();
35665             i = cols.pop();
35666             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
35667         }
35668         this.updateColumns();
35669         this.layout();
35670     },
35671
35672     onRowSelect : function(rowIndex){
35673         var row = this.getRowComposite(rowIndex);
35674         row.addClass("x-grid-row-selected");
35675     },
35676
35677     onRowDeselect : function(rowIndex){
35678         var row = this.getRowComposite(rowIndex);
35679         row.removeClass("x-grid-row-selected");
35680     },
35681
35682     onCellSelect : function(row, col){
35683         var cell = this.getCell(row, col);
35684         if(cell){
35685             Roo.fly(cell).addClass("x-grid-cell-selected");
35686         }
35687     },
35688
35689     onCellDeselect : function(row, col){
35690         var cell = this.getCell(row, col);
35691         if(cell){
35692             Roo.fly(cell).removeClass("x-grid-cell-selected");
35693         }
35694     },
35695
35696     updateHeaderSortState : function(){
35697         
35698         // sort state can be single { field: xxx, direction : yyy}
35699         // or   { xxx=>ASC , yyy : DESC ..... }
35700         
35701         var mstate = {};
35702         if (!this.ds.multiSort) { 
35703             var state = this.ds.getSortState();
35704             if(!state){
35705                 return;
35706             }
35707             mstate[state.field] = state.direction;
35708             // FIXME... - this is not used here.. but might be elsewhere..
35709             this.sortState = state;
35710             
35711         } else {
35712             mstate = this.ds.sortToggle;
35713         }
35714         //remove existing sort classes..
35715         
35716         var sc = this.sortClasses;
35717         var hds = this.el.select(this.headerSelector).removeClass(sc);
35718         
35719         for(var f in mstate) {
35720         
35721             var sortColumn = this.cm.findColumnIndex(f);
35722             
35723             if(sortColumn != -1){
35724                 var sortDir = mstate[f];        
35725                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
35726             }
35727         }
35728         
35729          
35730         
35731     },
35732
35733
35734     handleHeaderClick : function(g, index){
35735         if(this.headersDisabled){
35736             return;
35737         }
35738         var dm = g.dataSource, cm = g.colModel;
35739         if(!cm.isSortable(index)){
35740             return;
35741         }
35742         g.stopEditing();
35743         
35744         if (dm.multiSort) {
35745             // update the sortOrder
35746             var so = [];
35747             for(var i = 0; i < cm.config.length; i++ ) {
35748                 
35749                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
35750                     continue; // dont' bother, it's not in sort list or being set.
35751                 }
35752                 
35753                 so.push(cm.config[i].dataIndex);
35754             };
35755             dm.sortOrder = so;
35756         }
35757         
35758         
35759         dm.sort(cm.getDataIndex(index));
35760     },
35761
35762
35763     destroy : function(){
35764         if(this.colMenu){
35765             this.colMenu.removeAll();
35766             Roo.menu.MenuMgr.unregister(this.colMenu);
35767             this.colMenu.getEl().remove();
35768             delete this.colMenu;
35769         }
35770         if(this.hmenu){
35771             this.hmenu.removeAll();
35772             Roo.menu.MenuMgr.unregister(this.hmenu);
35773             this.hmenu.getEl().remove();
35774             delete this.hmenu;
35775         }
35776         if(this.grid.enableColumnMove){
35777             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35778             if(dds){
35779                 for(var dd in dds){
35780                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
35781                         var elid = dds[dd].dragElId;
35782                         dds[dd].unreg();
35783                         Roo.get(elid).remove();
35784                     } else if(dds[dd].config.isTarget){
35785                         dds[dd].proxyTop.remove();
35786                         dds[dd].proxyBottom.remove();
35787                         dds[dd].unreg();
35788                     }
35789                     if(Roo.dd.DDM.locationCache[dd]){
35790                         delete Roo.dd.DDM.locationCache[dd];
35791                     }
35792                 }
35793                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35794             }
35795         }
35796         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
35797         this.bind(null, null);
35798         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
35799     },
35800
35801     handleLockChange : function(){
35802         this.refresh(true);
35803     },
35804
35805     onDenyColumnLock : function(){
35806
35807     },
35808
35809     onDenyColumnHide : function(){
35810
35811     },
35812
35813     handleHdMenuClick : function(item){
35814         var index = this.hdCtxIndex;
35815         var cm = this.cm, ds = this.ds;
35816         switch(item.id){
35817             case "asc":
35818                 ds.sort(cm.getDataIndex(index), "ASC");
35819                 break;
35820             case "desc":
35821                 ds.sort(cm.getDataIndex(index), "DESC");
35822                 break;
35823             case "lock":
35824                 var lc = cm.getLockedCount();
35825                 if(cm.getColumnCount(true) <= lc+1){
35826                     this.onDenyColumnLock();
35827                     return;
35828                 }
35829                 if(lc != index){
35830                     cm.setLocked(index, true, true);
35831                     cm.moveColumn(index, lc);
35832                     this.grid.fireEvent("columnmove", index, lc);
35833                 }else{
35834                     cm.setLocked(index, true);
35835                 }
35836             break;
35837             case "unlock":
35838                 var lc = cm.getLockedCount();
35839                 if((lc-1) != index){
35840                     cm.setLocked(index, false, true);
35841                     cm.moveColumn(index, lc-1);
35842                     this.grid.fireEvent("columnmove", index, lc-1);
35843                 }else{
35844                     cm.setLocked(index, false);
35845                 }
35846             break;
35847             default:
35848                 index = cm.getIndexById(item.id.substr(4));
35849                 if(index != -1){
35850                     if(item.checked && cm.getColumnCount(true) <= 1){
35851                         this.onDenyColumnHide();
35852                         return false;
35853                     }
35854                     cm.setHidden(index, item.checked);
35855                 }
35856         }
35857         return true;
35858     },
35859
35860     beforeColMenuShow : function(){
35861         var cm = this.cm,  colCount = cm.getColumnCount();
35862         this.colMenu.removeAll();
35863         for(var i = 0; i < colCount; i++){
35864             this.colMenu.add(new Roo.menu.CheckItem({
35865                 id: "col-"+cm.getColumnId(i),
35866                 text: cm.getColumnHeader(i),
35867                 checked: !cm.isHidden(i),
35868                 hideOnClick:false
35869             }));
35870         }
35871     },
35872
35873     handleHdCtx : function(g, index, e){
35874         e.stopEvent();
35875         var hd = this.getHeaderCell(index);
35876         this.hdCtxIndex = index;
35877         var ms = this.hmenu.items, cm = this.cm;
35878         ms.get("asc").setDisabled(!cm.isSortable(index));
35879         ms.get("desc").setDisabled(!cm.isSortable(index));
35880         if(this.grid.enableColLock !== false){
35881             ms.get("lock").setDisabled(cm.isLocked(index));
35882             ms.get("unlock").setDisabled(!cm.isLocked(index));
35883         }
35884         this.hmenu.show(hd, "tl-bl");
35885     },
35886
35887     handleHdOver : function(e){
35888         var hd = this.findHeaderCell(e.getTarget());
35889         if(hd && !this.headersDisabled){
35890             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
35891                this.fly(hd).addClass("x-grid-hd-over");
35892             }
35893         }
35894     },
35895
35896     handleHdOut : function(e){
35897         var hd = this.findHeaderCell(e.getTarget());
35898         if(hd){
35899             this.fly(hd).removeClass("x-grid-hd-over");
35900         }
35901     },
35902
35903     handleSplitDblClick : function(e, t){
35904         var i = this.getCellIndex(t);
35905         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
35906             this.autoSizeColumn(i, true);
35907             this.layout();
35908         }
35909     },
35910
35911     render : function(){
35912
35913         var cm = this.cm;
35914         var colCount = cm.getColumnCount();
35915
35916         if(this.grid.monitorWindowResize === true){
35917             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35918         }
35919         var header = this.renderHeaders();
35920         var body = this.templates.body.apply({rows:""});
35921         var html = this.templates.master.apply({
35922             lockedBody: body,
35923             body: body,
35924             lockedHeader: header[0],
35925             header: header[1]
35926         });
35927
35928         //this.updateColumns();
35929
35930         this.grid.getGridEl().dom.innerHTML = html;
35931
35932         this.initElements();
35933         
35934         // a kludge to fix the random scolling effect in webkit
35935         this.el.on("scroll", function() {
35936             this.el.dom.scrollTop=0; // hopefully not recursive..
35937         },this);
35938
35939         this.scroller.on("scroll", this.handleScroll, this);
35940         this.lockedBody.on("mousewheel", this.handleWheel, this);
35941         this.mainBody.on("mousewheel", this.handleWheel, this);
35942
35943         this.mainHd.on("mouseover", this.handleHdOver, this);
35944         this.mainHd.on("mouseout", this.handleHdOut, this);
35945         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
35946                 {delegate: "."+this.splitClass});
35947
35948         this.lockedHd.on("mouseover", this.handleHdOver, this);
35949         this.lockedHd.on("mouseout", this.handleHdOut, this);
35950         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
35951                 {delegate: "."+this.splitClass});
35952
35953         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
35954             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35955         }
35956
35957         this.updateSplitters();
35958
35959         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
35960             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35961             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35962         }
35963
35964         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
35965             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
35966             this.hmenu.add(
35967                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
35968                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
35969             );
35970             if(this.grid.enableColLock !== false){
35971                 this.hmenu.add('-',
35972                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
35973                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
35974                 );
35975             }
35976             if(this.grid.enableColumnHide !== false){
35977
35978                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
35979                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
35980                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
35981
35982                 this.hmenu.add('-',
35983                     {id:"columns", text: this.columnsText, menu: this.colMenu}
35984                 );
35985             }
35986             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
35987
35988             this.grid.on("headercontextmenu", this.handleHdCtx, this);
35989         }
35990
35991         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
35992             this.dd = new Roo.grid.GridDragZone(this.grid, {
35993                 ddGroup : this.grid.ddGroup || 'GridDD'
35994             });
35995         }
35996
35997         /*
35998         for(var i = 0; i < colCount; i++){
35999             if(cm.isHidden(i)){
36000                 this.hideColumn(i);
36001             }
36002             if(cm.config[i].align){
36003                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
36004                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
36005             }
36006         }*/
36007         
36008         this.updateHeaderSortState();
36009
36010         this.beforeInitialResize();
36011         this.layout(true);
36012
36013         // two part rendering gives faster view to the user
36014         this.renderPhase2.defer(1, this);
36015     },
36016
36017     renderPhase2 : function(){
36018         // render the rows now
36019         this.refresh();
36020         if(this.grid.autoSizeColumns){
36021             this.autoSizeColumns();
36022         }
36023     },
36024
36025     beforeInitialResize : function(){
36026
36027     },
36028
36029     onColumnSplitterMoved : function(i, w){
36030         this.userResized = true;
36031         var cm = this.grid.colModel;
36032         cm.setColumnWidth(i, w, true);
36033         var cid = cm.getColumnId(i);
36034         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36035         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36036         this.updateSplitters();
36037         this.layout();
36038         this.grid.fireEvent("columnresize", i, w);
36039     },
36040
36041     syncRowHeights : function(startIndex, endIndex){
36042         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
36043             startIndex = startIndex || 0;
36044             var mrows = this.getBodyTable().rows;
36045             var lrows = this.getLockedTable().rows;
36046             var len = mrows.length-1;
36047             endIndex = Math.min(endIndex || len, len);
36048             for(var i = startIndex; i <= endIndex; i++){
36049                 var m = mrows[i], l = lrows[i];
36050                 var h = Math.max(m.offsetHeight, l.offsetHeight);
36051                 m.style.height = l.style.height = h + "px";
36052             }
36053         }
36054     },
36055
36056     layout : function(initialRender, is2ndPass){
36057         var g = this.grid;
36058         var auto = g.autoHeight;
36059         var scrollOffset = 16;
36060         var c = g.getGridEl(), cm = this.cm,
36061                 expandCol = g.autoExpandColumn,
36062                 gv = this;
36063         //c.beginMeasure();
36064
36065         if(!c.dom.offsetWidth){ // display:none?
36066             if(initialRender){
36067                 this.lockedWrap.show();
36068                 this.mainWrap.show();
36069             }
36070             return;
36071         }
36072
36073         var hasLock = this.cm.isLocked(0);
36074
36075         var tbh = this.headerPanel.getHeight();
36076         var bbh = this.footerPanel.getHeight();
36077
36078         if(auto){
36079             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
36080             var newHeight = ch + c.getBorderWidth("tb");
36081             if(g.maxHeight){
36082                 newHeight = Math.min(g.maxHeight, newHeight);
36083             }
36084             c.setHeight(newHeight);
36085         }
36086
36087         if(g.autoWidth){
36088             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
36089         }
36090
36091         var s = this.scroller;
36092
36093         var csize = c.getSize(true);
36094
36095         this.el.setSize(csize.width, csize.height);
36096
36097         this.headerPanel.setWidth(csize.width);
36098         this.footerPanel.setWidth(csize.width);
36099
36100         var hdHeight = this.mainHd.getHeight();
36101         var vw = csize.width;
36102         var vh = csize.height - (tbh + bbh);
36103
36104         s.setSize(vw, vh);
36105
36106         var bt = this.getBodyTable();
36107         var ltWidth = hasLock ?
36108                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
36109
36110         var scrollHeight = bt.offsetHeight;
36111         var scrollWidth = ltWidth + bt.offsetWidth;
36112         var vscroll = false, hscroll = false;
36113
36114         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
36115
36116         var lw = this.lockedWrap, mw = this.mainWrap;
36117         var lb = this.lockedBody, mb = this.mainBody;
36118
36119         setTimeout(function(){
36120             var t = s.dom.offsetTop;
36121             var w = s.dom.clientWidth,
36122                 h = s.dom.clientHeight;
36123
36124             lw.setTop(t);
36125             lw.setSize(ltWidth, h);
36126
36127             mw.setLeftTop(ltWidth, t);
36128             mw.setSize(w-ltWidth, h);
36129
36130             lb.setHeight(h-hdHeight);
36131             mb.setHeight(h-hdHeight);
36132
36133             if(is2ndPass !== true && !gv.userResized && expandCol){
36134                 // high speed resize without full column calculation
36135                 
36136                 var ci = cm.getIndexById(expandCol);
36137                 if (ci < 0) {
36138                     ci = cm.findColumnIndex(expandCol);
36139                 }
36140                 ci = Math.max(0, ci); // make sure it's got at least the first col.
36141                 var expandId = cm.getColumnId(ci);
36142                 var  tw = cm.getTotalWidth(false);
36143                 var currentWidth = cm.getColumnWidth(ci);
36144                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
36145                 if(currentWidth != cw){
36146                     cm.setColumnWidth(ci, cw, true);
36147                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36148                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36149                     gv.updateSplitters();
36150                     gv.layout(false, true);
36151                 }
36152             }
36153
36154             if(initialRender){
36155                 lw.show();
36156                 mw.show();
36157             }
36158             //c.endMeasure();
36159         }, 10);
36160     },
36161
36162     onWindowResize : function(){
36163         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
36164             return;
36165         }
36166         this.layout();
36167     },
36168
36169     appendFooter : function(parentEl){
36170         return null;
36171     },
36172
36173     sortAscText : "Sort Ascending",
36174     sortDescText : "Sort Descending",
36175     lockText : "Lock Column",
36176     unlockText : "Unlock Column",
36177     columnsText : "Columns"
36178 });
36179
36180
36181 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
36182     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
36183     this.proxy.el.addClass('x-grid3-col-dd');
36184 };
36185
36186 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
36187     handleMouseDown : function(e){
36188
36189     },
36190
36191     callHandleMouseDown : function(e){
36192         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
36193     }
36194 });
36195 /*
36196  * Based on:
36197  * Ext JS Library 1.1.1
36198  * Copyright(c) 2006-2007, Ext JS, LLC.
36199  *
36200  * Originally Released Under LGPL - original licence link has changed is not relivant.
36201  *
36202  * Fork - LGPL
36203  * <script type="text/javascript">
36204  */
36205  
36206 // private
36207 // This is a support class used internally by the Grid components
36208 Roo.grid.SplitDragZone = function(grid, hd, hd2){
36209     this.grid = grid;
36210     this.view = grid.getView();
36211     this.proxy = this.view.resizeProxy;
36212     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
36213         "gridSplitters" + this.grid.getGridEl().id, {
36214         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
36215     });
36216     this.setHandleElId(Roo.id(hd));
36217     this.setOuterHandleElId(Roo.id(hd2));
36218     this.scroll = false;
36219 };
36220 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
36221     fly: Roo.Element.fly,
36222
36223     b4StartDrag : function(x, y){
36224         this.view.headersDisabled = true;
36225         this.proxy.setHeight(this.view.mainWrap.getHeight());
36226         var w = this.cm.getColumnWidth(this.cellIndex);
36227         var minw = Math.max(w-this.grid.minColumnWidth, 0);
36228         this.resetConstraints();
36229         this.setXConstraint(minw, 1000);
36230         this.setYConstraint(0, 0);
36231         this.minX = x - minw;
36232         this.maxX = x + 1000;
36233         this.startPos = x;
36234         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
36235     },
36236
36237
36238     handleMouseDown : function(e){
36239         ev = Roo.EventObject.setEvent(e);
36240         var t = this.fly(ev.getTarget());
36241         if(t.hasClass("x-grid-split")){
36242             this.cellIndex = this.view.getCellIndex(t.dom);
36243             this.split = t.dom;
36244             this.cm = this.grid.colModel;
36245             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
36246                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
36247             }
36248         }
36249     },
36250
36251     endDrag : function(e){
36252         this.view.headersDisabled = false;
36253         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
36254         var diff = endX - this.startPos;
36255         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
36256     },
36257
36258     autoOffset : function(){
36259         this.setDelta(0,0);
36260     }
36261 });/*
36262  * Based on:
36263  * Ext JS Library 1.1.1
36264  * Copyright(c) 2006-2007, Ext JS, LLC.
36265  *
36266  * Originally Released Under LGPL - original licence link has changed is not relivant.
36267  *
36268  * Fork - LGPL
36269  * <script type="text/javascript">
36270  */
36271  
36272 // private
36273 // This is a support class used internally by the Grid components
36274 Roo.grid.GridDragZone = function(grid, config){
36275     this.view = grid.getView();
36276     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
36277     if(this.view.lockedBody){
36278         this.setHandleElId(Roo.id(this.view.mainBody.dom));
36279         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
36280     }
36281     this.scroll = false;
36282     this.grid = grid;
36283     this.ddel = document.createElement('div');
36284     this.ddel.className = 'x-grid-dd-wrap';
36285 };
36286
36287 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
36288     ddGroup : "GridDD",
36289
36290     getDragData : function(e){
36291         var t = Roo.lib.Event.getTarget(e);
36292         var rowIndex = this.view.findRowIndex(t);
36293         if(rowIndex !== false){
36294             var sm = this.grid.selModel;
36295             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
36296               //  sm.mouseDown(e, t);
36297             //}
36298             if (e.hasModifier()){
36299                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
36300             }
36301             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
36302         }
36303         return false;
36304     },
36305
36306     onInitDrag : function(e){
36307         var data = this.dragData;
36308         this.ddel.innerHTML = this.grid.getDragDropText();
36309         this.proxy.update(this.ddel);
36310         // fire start drag?
36311     },
36312
36313     afterRepair : function(){
36314         this.dragging = false;
36315     },
36316
36317     getRepairXY : function(e, data){
36318         return false;
36319     },
36320
36321     onEndDrag : function(data, e){
36322         // fire end drag?
36323     },
36324
36325     onValidDrop : function(dd, e, id){
36326         // fire drag drop?
36327         this.hideProxy();
36328     },
36329
36330     beforeInvalidDrop : function(e, id){
36331
36332     }
36333 });/*
36334  * Based on:
36335  * Ext JS Library 1.1.1
36336  * Copyright(c) 2006-2007, Ext JS, LLC.
36337  *
36338  * Originally Released Under LGPL - original licence link has changed is not relivant.
36339  *
36340  * Fork - LGPL
36341  * <script type="text/javascript">
36342  */
36343  
36344
36345 /**
36346  * @class Roo.grid.ColumnModel
36347  * @extends Roo.util.Observable
36348  * This is the default implementation of a ColumnModel used by the Grid. It defines
36349  * the columns in the grid.
36350  * <br>Usage:<br>
36351  <pre><code>
36352  var colModel = new Roo.grid.ColumnModel([
36353         {header: "Ticker", width: 60, sortable: true, locked: true},
36354         {header: "Company Name", width: 150, sortable: true},
36355         {header: "Market Cap.", width: 100, sortable: true},
36356         {header: "$ Sales", width: 100, sortable: true, renderer: money},
36357         {header: "Employees", width: 100, sortable: true, resizable: false}
36358  ]);
36359  </code></pre>
36360  * <p>
36361  
36362  * The config options listed for this class are options which may appear in each
36363  * individual column definition.
36364  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
36365  * @constructor
36366  * @param {Object} config An Array of column config objects. See this class's
36367  * config objects for details.
36368 */
36369 Roo.grid.ColumnModel = function(config){
36370         /**
36371      * The config passed into the constructor
36372      */
36373     this.config = config;
36374     this.lookup = {};
36375
36376     // if no id, create one
36377     // if the column does not have a dataIndex mapping,
36378     // map it to the order it is in the config
36379     for(var i = 0, len = config.length; i < len; i++){
36380         var c = config[i];
36381         if(typeof c.dataIndex == "undefined"){
36382             c.dataIndex = i;
36383         }
36384         if(typeof c.renderer == "string"){
36385             c.renderer = Roo.util.Format[c.renderer];
36386         }
36387         if(typeof c.id == "undefined"){
36388             c.id = Roo.id();
36389         }
36390         if(c.editor && c.editor.xtype){
36391             c.editor  = Roo.factory(c.editor, Roo.grid);
36392         }
36393         if(c.editor && c.editor.isFormField){
36394             c.editor = new Roo.grid.GridEditor(c.editor);
36395         }
36396         this.lookup[c.id] = c;
36397     }
36398
36399     /**
36400      * The width of columns which have no width specified (defaults to 100)
36401      * @type Number
36402      */
36403     this.defaultWidth = 100;
36404
36405     /**
36406      * Default sortable of columns which have no sortable specified (defaults to false)
36407      * @type Boolean
36408      */
36409     this.defaultSortable = false;
36410
36411     this.addEvents({
36412         /**
36413              * @event widthchange
36414              * Fires when the width of a column changes.
36415              * @param {ColumnModel} this
36416              * @param {Number} columnIndex The column index
36417              * @param {Number} newWidth The new width
36418              */
36419             "widthchange": true,
36420         /**
36421              * @event headerchange
36422              * Fires when the text of a header changes.
36423              * @param {ColumnModel} this
36424              * @param {Number} columnIndex The column index
36425              * @param {Number} newText The new header text
36426              */
36427             "headerchange": true,
36428         /**
36429              * @event hiddenchange
36430              * Fires when a column is hidden or "unhidden".
36431              * @param {ColumnModel} this
36432              * @param {Number} columnIndex The column index
36433              * @param {Boolean} hidden true if hidden, false otherwise
36434              */
36435             "hiddenchange": true,
36436             /**
36437          * @event columnmoved
36438          * Fires when a column is moved.
36439          * @param {ColumnModel} this
36440          * @param {Number} oldIndex
36441          * @param {Number} newIndex
36442          */
36443         "columnmoved" : true,
36444         /**
36445          * @event columlockchange
36446          * Fires when a column's locked state is changed
36447          * @param {ColumnModel} this
36448          * @param {Number} colIndex
36449          * @param {Boolean} locked true if locked
36450          */
36451         "columnlockchange" : true
36452     });
36453     Roo.grid.ColumnModel.superclass.constructor.call(this);
36454 };
36455 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
36456     /**
36457      * @cfg {String} header The header text to display in the Grid view.
36458      */
36459     /**
36460      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
36461      * {@link Roo.data.Record} definition from which to draw the column's value. If not
36462      * specified, the column's index is used as an index into the Record's data Array.
36463      */
36464     /**
36465      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
36466      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
36467      */
36468     /**
36469      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
36470      * Defaults to the value of the {@link #defaultSortable} property.
36471      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
36472      */
36473     /**
36474      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
36475      */
36476     /**
36477      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
36478      */
36479     /**
36480      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
36481      */
36482     /**
36483      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
36484      */
36485     /**
36486      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
36487      * given the cell's data value. See {@link #setRenderer}. If not specified, the
36488      * default renderer uses the raw data value.
36489      */
36490        /**
36491      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
36492      */
36493     /**
36494      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
36495      */
36496
36497     /**
36498      * Returns the id of the column at the specified index.
36499      * @param {Number} index The column index
36500      * @return {String} the id
36501      */
36502     getColumnId : function(index){
36503         return this.config[index].id;
36504     },
36505
36506     /**
36507      * Returns the column for a specified id.
36508      * @param {String} id The column id
36509      * @return {Object} the column
36510      */
36511     getColumnById : function(id){
36512         return this.lookup[id];
36513     },
36514
36515     
36516     /**
36517      * Returns the column for a specified dataIndex.
36518      * @param {String} dataIndex The column dataIndex
36519      * @return {Object|Boolean} the column or false if not found
36520      */
36521     getColumnByDataIndex: function(dataIndex){
36522         var index = this.findColumnIndex(dataIndex);
36523         return index > -1 ? this.config[index] : false;
36524     },
36525     
36526     /**
36527      * Returns the index for a specified column id.
36528      * @param {String} id The column id
36529      * @return {Number} the index, or -1 if not found
36530      */
36531     getIndexById : function(id){
36532         for(var i = 0, len = this.config.length; i < len; i++){
36533             if(this.config[i].id == id){
36534                 return i;
36535             }
36536         }
36537         return -1;
36538     },
36539     
36540     /**
36541      * Returns the index for a specified column dataIndex.
36542      * @param {String} dataIndex The column dataIndex
36543      * @return {Number} the index, or -1 if not found
36544      */
36545     
36546     findColumnIndex : function(dataIndex){
36547         for(var i = 0, len = this.config.length; i < len; i++){
36548             if(this.config[i].dataIndex == dataIndex){
36549                 return i;
36550             }
36551         }
36552         return -1;
36553     },
36554     
36555     
36556     moveColumn : function(oldIndex, newIndex){
36557         var c = this.config[oldIndex];
36558         this.config.splice(oldIndex, 1);
36559         this.config.splice(newIndex, 0, c);
36560         this.dataMap = null;
36561         this.fireEvent("columnmoved", this, oldIndex, newIndex);
36562     },
36563
36564     isLocked : function(colIndex){
36565         return this.config[colIndex].locked === true;
36566     },
36567
36568     setLocked : function(colIndex, value, suppressEvent){
36569         if(this.isLocked(colIndex) == value){
36570             return;
36571         }
36572         this.config[colIndex].locked = value;
36573         if(!suppressEvent){
36574             this.fireEvent("columnlockchange", this, colIndex, value);
36575         }
36576     },
36577
36578     getTotalLockedWidth : function(){
36579         var totalWidth = 0;
36580         for(var i = 0; i < this.config.length; i++){
36581             if(this.isLocked(i) && !this.isHidden(i)){
36582                 this.totalWidth += this.getColumnWidth(i);
36583             }
36584         }
36585         return totalWidth;
36586     },
36587
36588     getLockedCount : function(){
36589         for(var i = 0, len = this.config.length; i < len; i++){
36590             if(!this.isLocked(i)){
36591                 return i;
36592             }
36593         }
36594     },
36595
36596     /**
36597      * Returns the number of columns.
36598      * @return {Number}
36599      */
36600     getColumnCount : function(visibleOnly){
36601         if(visibleOnly === true){
36602             var c = 0;
36603             for(var i = 0, len = this.config.length; i < len; i++){
36604                 if(!this.isHidden(i)){
36605                     c++;
36606                 }
36607             }
36608             return c;
36609         }
36610         return this.config.length;
36611     },
36612
36613     /**
36614      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
36615      * @param {Function} fn
36616      * @param {Object} scope (optional)
36617      * @return {Array} result
36618      */
36619     getColumnsBy : function(fn, scope){
36620         var r = [];
36621         for(var i = 0, len = this.config.length; i < len; i++){
36622             var c = this.config[i];
36623             if(fn.call(scope||this, c, i) === true){
36624                 r[r.length] = c;
36625             }
36626         }
36627         return r;
36628     },
36629
36630     /**
36631      * Returns true if the specified column is sortable.
36632      * @param {Number} col The column index
36633      * @return {Boolean}
36634      */
36635     isSortable : function(col){
36636         if(typeof this.config[col].sortable == "undefined"){
36637             return this.defaultSortable;
36638         }
36639         return this.config[col].sortable;
36640     },
36641
36642     /**
36643      * Returns the rendering (formatting) function defined for the column.
36644      * @param {Number} col The column index.
36645      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
36646      */
36647     getRenderer : function(col){
36648         if(!this.config[col].renderer){
36649             return Roo.grid.ColumnModel.defaultRenderer;
36650         }
36651         return this.config[col].renderer;
36652     },
36653
36654     /**
36655      * Sets the rendering (formatting) function for a column.
36656      * @param {Number} col The column index
36657      * @param {Function} fn The function to use to process the cell's raw data
36658      * to return HTML markup for the grid view. The render function is called with
36659      * the following parameters:<ul>
36660      * <li>Data value.</li>
36661      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
36662      * <li>css A CSS style string to apply to the table cell.</li>
36663      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
36664      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
36665      * <li>Row index</li>
36666      * <li>Column index</li>
36667      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
36668      */
36669     setRenderer : function(col, fn){
36670         this.config[col].renderer = fn;
36671     },
36672
36673     /**
36674      * Returns the width for the specified column.
36675      * @param {Number} col The column index
36676      * @return {Number}
36677      */
36678     getColumnWidth : function(col){
36679         return this.config[col].width * 1 || this.defaultWidth;
36680     },
36681
36682     /**
36683      * Sets the width for a column.
36684      * @param {Number} col The column index
36685      * @param {Number} width The new width
36686      */
36687     setColumnWidth : function(col, width, suppressEvent){
36688         this.config[col].width = width;
36689         this.totalWidth = null;
36690         if(!suppressEvent){
36691              this.fireEvent("widthchange", this, col, width);
36692         }
36693     },
36694
36695     /**
36696      * Returns the total width of all columns.
36697      * @param {Boolean} includeHidden True to include hidden column widths
36698      * @return {Number}
36699      */
36700     getTotalWidth : function(includeHidden){
36701         if(!this.totalWidth){
36702             this.totalWidth = 0;
36703             for(var i = 0, len = this.config.length; i < len; i++){
36704                 if(includeHidden || !this.isHidden(i)){
36705                     this.totalWidth += this.getColumnWidth(i);
36706                 }
36707             }
36708         }
36709         return this.totalWidth;
36710     },
36711
36712     /**
36713      * Returns the header for the specified column.
36714      * @param {Number} col The column index
36715      * @return {String}
36716      */
36717     getColumnHeader : function(col){
36718         return this.config[col].header;
36719     },
36720
36721     /**
36722      * Sets the header for a column.
36723      * @param {Number} col The column index
36724      * @param {String} header The new header
36725      */
36726     setColumnHeader : function(col, header){
36727         this.config[col].header = header;
36728         this.fireEvent("headerchange", this, col, header);
36729     },
36730
36731     /**
36732      * Returns the tooltip for the specified column.
36733      * @param {Number} col The column index
36734      * @return {String}
36735      */
36736     getColumnTooltip : function(col){
36737             return this.config[col].tooltip;
36738     },
36739     /**
36740      * Sets the tooltip for a column.
36741      * @param {Number} col The column index
36742      * @param {String} tooltip The new tooltip
36743      */
36744     setColumnTooltip : function(col, tooltip){
36745             this.config[col].tooltip = tooltip;
36746     },
36747
36748     /**
36749      * Returns the dataIndex for the specified column.
36750      * @param {Number} col The column index
36751      * @return {Number}
36752      */
36753     getDataIndex : function(col){
36754         return this.config[col].dataIndex;
36755     },
36756
36757     /**
36758      * Sets the dataIndex for a column.
36759      * @param {Number} col The column index
36760      * @param {Number} dataIndex The new dataIndex
36761      */
36762     setDataIndex : function(col, dataIndex){
36763         this.config[col].dataIndex = dataIndex;
36764     },
36765
36766     
36767     
36768     /**
36769      * Returns true if the cell is editable.
36770      * @param {Number} colIndex The column index
36771      * @param {Number} rowIndex The row index
36772      * @return {Boolean}
36773      */
36774     isCellEditable : function(colIndex, rowIndex){
36775         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
36776     },
36777
36778     /**
36779      * Returns the editor defined for the cell/column.
36780      * return false or null to disable editing.
36781      * @param {Number} colIndex The column index
36782      * @param {Number} rowIndex The row index
36783      * @return {Object}
36784      */
36785     getCellEditor : function(colIndex, rowIndex){
36786         return this.config[colIndex].editor;
36787     },
36788
36789     /**
36790      * Sets if a column is editable.
36791      * @param {Number} col The column index
36792      * @param {Boolean} editable True if the column is editable
36793      */
36794     setEditable : function(col, editable){
36795         this.config[col].editable = editable;
36796     },
36797
36798
36799     /**
36800      * Returns true if the column is hidden.
36801      * @param {Number} colIndex The column index
36802      * @return {Boolean}
36803      */
36804     isHidden : function(colIndex){
36805         return this.config[colIndex].hidden;
36806     },
36807
36808
36809     /**
36810      * Returns true if the column width cannot be changed
36811      */
36812     isFixed : function(colIndex){
36813         return this.config[colIndex].fixed;
36814     },
36815
36816     /**
36817      * Returns true if the column can be resized
36818      * @return {Boolean}
36819      */
36820     isResizable : function(colIndex){
36821         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
36822     },
36823     /**
36824      * Sets if a column is hidden.
36825      * @param {Number} colIndex The column index
36826      * @param {Boolean} hidden True if the column is hidden
36827      */
36828     setHidden : function(colIndex, hidden){
36829         this.config[colIndex].hidden = hidden;
36830         this.totalWidth = null;
36831         this.fireEvent("hiddenchange", this, colIndex, hidden);
36832     },
36833
36834     /**
36835      * Sets the editor for a column.
36836      * @param {Number} col The column index
36837      * @param {Object} editor The editor object
36838      */
36839     setEditor : function(col, editor){
36840         this.config[col].editor = editor;
36841     }
36842 });
36843
36844 Roo.grid.ColumnModel.defaultRenderer = function(value){
36845         if(typeof value == "string" && value.length < 1){
36846             return "&#160;";
36847         }
36848         return value;
36849 };
36850
36851 // Alias for backwards compatibility
36852 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
36853 /*
36854  * Based on:
36855  * Ext JS Library 1.1.1
36856  * Copyright(c) 2006-2007, Ext JS, LLC.
36857  *
36858  * Originally Released Under LGPL - original licence link has changed is not relivant.
36859  *
36860  * Fork - LGPL
36861  * <script type="text/javascript">
36862  */
36863
36864 /**
36865  * @class Roo.grid.AbstractSelectionModel
36866  * @extends Roo.util.Observable
36867  * Abstract base class for grid SelectionModels.  It provides the interface that should be
36868  * implemented by descendant classes.  This class should not be directly instantiated.
36869  * @constructor
36870  */
36871 Roo.grid.AbstractSelectionModel = function(){
36872     this.locked = false;
36873     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
36874 };
36875
36876 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
36877     /** @ignore Called by the grid automatically. Do not call directly. */
36878     init : function(grid){
36879         this.grid = grid;
36880         this.initEvents();
36881     },
36882
36883     /**
36884      * Locks the selections.
36885      */
36886     lock : function(){
36887         this.locked = true;
36888     },
36889
36890     /**
36891      * Unlocks the selections.
36892      */
36893     unlock : function(){
36894         this.locked = false;
36895     },
36896
36897     /**
36898      * Returns true if the selections are locked.
36899      * @return {Boolean}
36900      */
36901     isLocked : function(){
36902         return this.locked;
36903     }
36904 });/*
36905  * Based on:
36906  * Ext JS Library 1.1.1
36907  * Copyright(c) 2006-2007, Ext JS, LLC.
36908  *
36909  * Originally Released Under LGPL - original licence link has changed is not relivant.
36910  *
36911  * Fork - LGPL
36912  * <script type="text/javascript">
36913  */
36914 /**
36915  * @extends Roo.grid.AbstractSelectionModel
36916  * @class Roo.grid.RowSelectionModel
36917  * The default SelectionModel used by {@link Roo.grid.Grid}.
36918  * It supports multiple selections and keyboard selection/navigation. 
36919  * @constructor
36920  * @param {Object} config
36921  */
36922 Roo.grid.RowSelectionModel = function(config){
36923     Roo.apply(this, config);
36924     this.selections = new Roo.util.MixedCollection(false, function(o){
36925         return o.id;
36926     });
36927
36928     this.last = false;
36929     this.lastActive = false;
36930
36931     this.addEvents({
36932         /**
36933              * @event selectionchange
36934              * Fires when the selection changes
36935              * @param {SelectionModel} this
36936              */
36937             "selectionchange" : true,
36938         /**
36939              * @event afterselectionchange
36940              * Fires after the selection changes (eg. by key press or clicking)
36941              * @param {SelectionModel} this
36942              */
36943             "afterselectionchange" : true,
36944         /**
36945              * @event beforerowselect
36946              * Fires when a row is selected being selected, return false to cancel.
36947              * @param {SelectionModel} this
36948              * @param {Number} rowIndex The selected index
36949              * @param {Boolean} keepExisting False if other selections will be cleared
36950              */
36951             "beforerowselect" : true,
36952         /**
36953              * @event rowselect
36954              * Fires when a row is selected.
36955              * @param {SelectionModel} this
36956              * @param {Number} rowIndex The selected index
36957              * @param {Roo.data.Record} r The record
36958              */
36959             "rowselect" : true,
36960         /**
36961              * @event rowdeselect
36962              * Fires when a row is deselected.
36963              * @param {SelectionModel} this
36964              * @param {Number} rowIndex The selected index
36965              */
36966         "rowdeselect" : true
36967     });
36968     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
36969     this.locked = false;
36970 };
36971
36972 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
36973     /**
36974      * @cfg {Boolean} singleSelect
36975      * True to allow selection of only one row at a time (defaults to false)
36976      */
36977     singleSelect : false,
36978
36979     // private
36980     initEvents : function(){
36981
36982         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
36983             this.grid.on("mousedown", this.handleMouseDown, this);
36984         }else{ // allow click to work like normal
36985             this.grid.on("rowclick", this.handleDragableRowClick, this);
36986         }
36987
36988         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
36989             "up" : function(e){
36990                 if(!e.shiftKey){
36991                     this.selectPrevious(e.shiftKey);
36992                 }else if(this.last !== false && this.lastActive !== false){
36993                     var last = this.last;
36994                     this.selectRange(this.last,  this.lastActive-1);
36995                     this.grid.getView().focusRow(this.lastActive);
36996                     if(last !== false){
36997                         this.last = last;
36998                     }
36999                 }else{
37000                     this.selectFirstRow();
37001                 }
37002                 this.fireEvent("afterselectionchange", this);
37003             },
37004             "down" : function(e){
37005                 if(!e.shiftKey){
37006                     this.selectNext(e.shiftKey);
37007                 }else if(this.last !== false && this.lastActive !== false){
37008                     var last = this.last;
37009                     this.selectRange(this.last,  this.lastActive+1);
37010                     this.grid.getView().focusRow(this.lastActive);
37011                     if(last !== false){
37012                         this.last = last;
37013                     }
37014                 }else{
37015                     this.selectFirstRow();
37016                 }
37017                 this.fireEvent("afterselectionchange", this);
37018             },
37019             scope: this
37020         });
37021
37022         var view = this.grid.view;
37023         view.on("refresh", this.onRefresh, this);
37024         view.on("rowupdated", this.onRowUpdated, this);
37025         view.on("rowremoved", this.onRemove, this);
37026     },
37027
37028     // private
37029     onRefresh : function(){
37030         var ds = this.grid.dataSource, i, v = this.grid.view;
37031         var s = this.selections;
37032         s.each(function(r){
37033             if((i = ds.indexOfId(r.id)) != -1){
37034                 v.onRowSelect(i);
37035             }else{
37036                 s.remove(r);
37037             }
37038         });
37039     },
37040
37041     // private
37042     onRemove : function(v, index, r){
37043         this.selections.remove(r);
37044     },
37045
37046     // private
37047     onRowUpdated : function(v, index, r){
37048         if(this.isSelected(r)){
37049             v.onRowSelect(index);
37050         }
37051     },
37052
37053     /**
37054      * Select records.
37055      * @param {Array} records The records to select
37056      * @param {Boolean} keepExisting (optional) True to keep existing selections
37057      */
37058     selectRecords : function(records, keepExisting){
37059         if(!keepExisting){
37060             this.clearSelections();
37061         }
37062         var ds = this.grid.dataSource;
37063         for(var i = 0, len = records.length; i < len; i++){
37064             this.selectRow(ds.indexOf(records[i]), true);
37065         }
37066     },
37067
37068     /**
37069      * Gets the number of selected rows.
37070      * @return {Number}
37071      */
37072     getCount : function(){
37073         return this.selections.length;
37074     },
37075
37076     /**
37077      * Selects the first row in the grid.
37078      */
37079     selectFirstRow : function(){
37080         this.selectRow(0);
37081     },
37082
37083     /**
37084      * Select the last row.
37085      * @param {Boolean} keepExisting (optional) True to keep existing selections
37086      */
37087     selectLastRow : function(keepExisting){
37088         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
37089     },
37090
37091     /**
37092      * Selects the row immediately following the last selected row.
37093      * @param {Boolean} keepExisting (optional) True to keep existing selections
37094      */
37095     selectNext : function(keepExisting){
37096         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
37097             this.selectRow(this.last+1, keepExisting);
37098             this.grid.getView().focusRow(this.last);
37099         }
37100     },
37101
37102     /**
37103      * Selects the row that precedes the last selected row.
37104      * @param {Boolean} keepExisting (optional) True to keep existing selections
37105      */
37106     selectPrevious : function(keepExisting){
37107         if(this.last){
37108             this.selectRow(this.last-1, keepExisting);
37109             this.grid.getView().focusRow(this.last);
37110         }
37111     },
37112
37113     /**
37114      * Returns the selected records
37115      * @return {Array} Array of selected records
37116      */
37117     getSelections : function(){
37118         return [].concat(this.selections.items);
37119     },
37120
37121     /**
37122      * Returns the first selected record.
37123      * @return {Record}
37124      */
37125     getSelected : function(){
37126         return this.selections.itemAt(0);
37127     },
37128
37129
37130     /**
37131      * Clears all selections.
37132      */
37133     clearSelections : function(fast){
37134         if(this.locked) return;
37135         if(fast !== true){
37136             var ds = this.grid.dataSource;
37137             var s = this.selections;
37138             s.each(function(r){
37139                 this.deselectRow(ds.indexOfId(r.id));
37140             }, this);
37141             s.clear();
37142         }else{
37143             this.selections.clear();
37144         }
37145         this.last = false;
37146     },
37147
37148
37149     /**
37150      * Selects all rows.
37151      */
37152     selectAll : function(){
37153         if(this.locked) return;
37154         this.selections.clear();
37155         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
37156             this.selectRow(i, true);
37157         }
37158     },
37159
37160     /**
37161      * Returns True if there is a selection.
37162      * @return {Boolean}
37163      */
37164     hasSelection : function(){
37165         return this.selections.length > 0;
37166     },
37167
37168     /**
37169      * Returns True if the specified row is selected.
37170      * @param {Number/Record} record The record or index of the record to check
37171      * @return {Boolean}
37172      */
37173     isSelected : function(index){
37174         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
37175         return (r && this.selections.key(r.id) ? true : false);
37176     },
37177
37178     /**
37179      * Returns True if the specified record id is selected.
37180      * @param {String} id The id of record to check
37181      * @return {Boolean}
37182      */
37183     isIdSelected : function(id){
37184         return (this.selections.key(id) ? true : false);
37185     },
37186
37187     // private
37188     handleMouseDown : function(e, t){
37189         var view = this.grid.getView(), rowIndex;
37190         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
37191             return;
37192         };
37193         if(e.shiftKey && this.last !== false){
37194             var last = this.last;
37195             this.selectRange(last, rowIndex, e.ctrlKey);
37196             this.last = last; // reset the last
37197             view.focusRow(rowIndex);
37198         }else{
37199             var isSelected = this.isSelected(rowIndex);
37200             if(e.button !== 0 && isSelected){
37201                 view.focusRow(rowIndex);
37202             }else if(e.ctrlKey && isSelected){
37203                 this.deselectRow(rowIndex);
37204             }else if(!isSelected){
37205                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
37206                 view.focusRow(rowIndex);
37207             }
37208         }
37209         this.fireEvent("afterselectionchange", this);
37210     },
37211     // private
37212     handleDragableRowClick :  function(grid, rowIndex, e) 
37213     {
37214         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
37215             this.selectRow(rowIndex, false);
37216             grid.view.focusRow(rowIndex);
37217              this.fireEvent("afterselectionchange", this);
37218         }
37219     },
37220     
37221     /**
37222      * Selects multiple rows.
37223      * @param {Array} rows Array of the indexes of the row to select
37224      * @param {Boolean} keepExisting (optional) True to keep existing selections
37225      */
37226     selectRows : function(rows, keepExisting){
37227         if(!keepExisting){
37228             this.clearSelections();
37229         }
37230         for(var i = 0, len = rows.length; i < len; i++){
37231             this.selectRow(rows[i], true);
37232         }
37233     },
37234
37235     /**
37236      * Selects a range of rows. All rows in between startRow and endRow are also selected.
37237      * @param {Number} startRow The index of the first row in the range
37238      * @param {Number} endRow The index of the last row in the range
37239      * @param {Boolean} keepExisting (optional) True to retain existing selections
37240      */
37241     selectRange : function(startRow, endRow, keepExisting){
37242         if(this.locked) return;
37243         if(!keepExisting){
37244             this.clearSelections();
37245         }
37246         if(startRow <= endRow){
37247             for(var i = startRow; i <= endRow; i++){
37248                 this.selectRow(i, true);
37249             }
37250         }else{
37251             for(var i = startRow; i >= endRow; i--){
37252                 this.selectRow(i, true);
37253             }
37254         }
37255     },
37256
37257     /**
37258      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
37259      * @param {Number} startRow The index of the first row in the range
37260      * @param {Number} endRow The index of the last row in the range
37261      */
37262     deselectRange : function(startRow, endRow, preventViewNotify){
37263         if(this.locked) return;
37264         for(var i = startRow; i <= endRow; i++){
37265             this.deselectRow(i, preventViewNotify);
37266         }
37267     },
37268
37269     /**
37270      * Selects a row.
37271      * @param {Number} row The index of the row to select
37272      * @param {Boolean} keepExisting (optional) True to keep existing selections
37273      */
37274     selectRow : function(index, keepExisting, preventViewNotify){
37275         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
37276         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
37277             if(!keepExisting || this.singleSelect){
37278                 this.clearSelections();
37279             }
37280             var r = this.grid.dataSource.getAt(index);
37281             this.selections.add(r);
37282             this.last = this.lastActive = index;
37283             if(!preventViewNotify){
37284                 this.grid.getView().onRowSelect(index);
37285             }
37286             this.fireEvent("rowselect", this, index, r);
37287             this.fireEvent("selectionchange", this);
37288         }
37289     },
37290
37291     /**
37292      * Deselects a row.
37293      * @param {Number} row The index of the row to deselect
37294      */
37295     deselectRow : function(index, preventViewNotify){
37296         if(this.locked) return;
37297         if(this.last == index){
37298             this.last = false;
37299         }
37300         if(this.lastActive == index){
37301             this.lastActive = false;
37302         }
37303         var r = this.grid.dataSource.getAt(index);
37304         this.selections.remove(r);
37305         if(!preventViewNotify){
37306             this.grid.getView().onRowDeselect(index);
37307         }
37308         this.fireEvent("rowdeselect", this, index);
37309         this.fireEvent("selectionchange", this);
37310     },
37311
37312     // private
37313     restoreLast : function(){
37314         if(this._last){
37315             this.last = this._last;
37316         }
37317     },
37318
37319     // private
37320     acceptsNav : function(row, col, cm){
37321         return !cm.isHidden(col) && cm.isCellEditable(col, row);
37322     },
37323
37324     // private
37325     onEditorKey : function(field, e){
37326         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
37327         if(k == e.TAB){
37328             e.stopEvent();
37329             ed.completeEdit();
37330             if(e.shiftKey){
37331                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
37332             }else{
37333                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37334             }
37335         }else if(k == e.ENTER && !e.ctrlKey){
37336             e.stopEvent();
37337             ed.completeEdit();
37338             if(e.shiftKey){
37339                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
37340             }else{
37341                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
37342             }
37343         }else if(k == e.ESC){
37344             ed.cancelEdit();
37345         }
37346         if(newCell){
37347             g.startEditing(newCell[0], newCell[1]);
37348         }
37349     }
37350 });/*
37351  * Based on:
37352  * Ext JS Library 1.1.1
37353  * Copyright(c) 2006-2007, Ext JS, LLC.
37354  *
37355  * Originally Released Under LGPL - original licence link has changed is not relivant.
37356  *
37357  * Fork - LGPL
37358  * <script type="text/javascript">
37359  */
37360 /**
37361  * @class Roo.grid.CellSelectionModel
37362  * @extends Roo.grid.AbstractSelectionModel
37363  * This class provides the basic implementation for cell selection in a grid.
37364  * @constructor
37365  * @param {Object} config The object containing the configuration of this model.
37366  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
37367  */
37368 Roo.grid.CellSelectionModel = function(config){
37369     Roo.apply(this, config);
37370
37371     this.selection = null;
37372
37373     this.addEvents({
37374         /**
37375              * @event beforerowselect
37376              * Fires before a cell is selected.
37377              * @param {SelectionModel} this
37378              * @param {Number} rowIndex The selected row index
37379              * @param {Number} colIndex The selected cell index
37380              */
37381             "beforecellselect" : true,
37382         /**
37383              * @event cellselect
37384              * Fires when a cell is selected.
37385              * @param {SelectionModel} this
37386              * @param {Number} rowIndex The selected row index
37387              * @param {Number} colIndex The selected cell index
37388              */
37389             "cellselect" : true,
37390         /**
37391              * @event selectionchange
37392              * Fires when the active selection changes.
37393              * @param {SelectionModel} this
37394              * @param {Object} selection null for no selection or an object (o) with two properties
37395                 <ul>
37396                 <li>o.record: the record object for the row the selection is in</li>
37397                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
37398                 </ul>
37399              */
37400             "selectionchange" : true,
37401         /**
37402              * @event tabend
37403              * Fires when the tab (or enter) was pressed on the last editable cell
37404              * You can use this to trigger add new row.
37405              * @param {SelectionModel} this
37406              */
37407             "tabend" : true,
37408          /**
37409              * @event beforeeditnext
37410              * Fires before the next editable sell is made active
37411              * You can use this to skip to another cell or fire the tabend
37412              *    if you set cell to false
37413              * @param {Object} eventdata object : { cell : [ row, col ] } 
37414              */
37415             "beforeeditnext" : true
37416     });
37417     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
37418 };
37419
37420 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
37421     
37422     enter_is_tab: false,
37423
37424     /** @ignore */
37425     initEvents : function(){
37426         this.grid.on("mousedown", this.handleMouseDown, this);
37427         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
37428         var view = this.grid.view;
37429         view.on("refresh", this.onViewChange, this);
37430         view.on("rowupdated", this.onRowUpdated, this);
37431         view.on("beforerowremoved", this.clearSelections, this);
37432         view.on("beforerowsinserted", this.clearSelections, this);
37433         if(this.grid.isEditor){
37434             this.grid.on("beforeedit", this.beforeEdit,  this);
37435         }
37436     },
37437
37438         //private
37439     beforeEdit : function(e){
37440         this.select(e.row, e.column, false, true, e.record);
37441     },
37442
37443         //private
37444     onRowUpdated : function(v, index, r){
37445         if(this.selection && this.selection.record == r){
37446             v.onCellSelect(index, this.selection.cell[1]);
37447         }
37448     },
37449
37450         //private
37451     onViewChange : function(){
37452         this.clearSelections(true);
37453     },
37454
37455         /**
37456          * Returns the currently selected cell,.
37457          * @return {Array} The selected cell (row, column) or null if none selected.
37458          */
37459     getSelectedCell : function(){
37460         return this.selection ? this.selection.cell : null;
37461     },
37462
37463     /**
37464      * Clears all selections.
37465      * @param {Boolean} true to prevent the gridview from being notified about the change.
37466      */
37467     clearSelections : function(preventNotify){
37468         var s = this.selection;
37469         if(s){
37470             if(preventNotify !== true){
37471                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
37472             }
37473             this.selection = null;
37474             this.fireEvent("selectionchange", this, null);
37475         }
37476     },
37477
37478     /**
37479      * Returns true if there is a selection.
37480      * @return {Boolean}
37481      */
37482     hasSelection : function(){
37483         return this.selection ? true : false;
37484     },
37485
37486     /** @ignore */
37487     handleMouseDown : function(e, t){
37488         var v = this.grid.getView();
37489         if(this.isLocked()){
37490             return;
37491         };
37492         var row = v.findRowIndex(t);
37493         var cell = v.findCellIndex(t);
37494         if(row !== false && cell !== false){
37495             this.select(row, cell);
37496         }
37497     },
37498
37499     /**
37500      * Selects a cell.
37501      * @param {Number} rowIndex
37502      * @param {Number} collIndex
37503      */
37504     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
37505         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
37506             this.clearSelections();
37507             r = r || this.grid.dataSource.getAt(rowIndex);
37508             this.selection = {
37509                 record : r,
37510                 cell : [rowIndex, colIndex]
37511             };
37512             if(!preventViewNotify){
37513                 var v = this.grid.getView();
37514                 v.onCellSelect(rowIndex, colIndex);
37515                 if(preventFocus !== true){
37516                     v.focusCell(rowIndex, colIndex);
37517                 }
37518             }
37519             this.fireEvent("cellselect", this, rowIndex, colIndex);
37520             this.fireEvent("selectionchange", this, this.selection);
37521         }
37522     },
37523
37524         //private
37525     isSelectable : function(rowIndex, colIndex, cm){
37526         return !cm.isHidden(colIndex);
37527     },
37528
37529     /** @ignore */
37530     handleKeyDown : function(e){
37531         //Roo.log('Cell Sel Model handleKeyDown');
37532         if(!e.isNavKeyPress()){
37533             return;
37534         }
37535         var g = this.grid, s = this.selection;
37536         if(!s){
37537             e.stopEvent();
37538             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
37539             if(cell){
37540                 this.select(cell[0], cell[1]);
37541             }
37542             return;
37543         }
37544         var sm = this;
37545         var walk = function(row, col, step){
37546             return g.walkCells(row, col, step, sm.isSelectable,  sm);
37547         };
37548         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
37549         var newCell;
37550
37551       
37552
37553         switch(k){
37554             case e.TAB:
37555                 // handled by onEditorKey
37556                 if (g.isEditor && g.editing) {
37557                     return;
37558                 }
37559                 if(e.shiftKey) {
37560                     newCell = walk(r, c-1, -1);
37561                 } else {
37562                     newCell = walk(r, c+1, 1);
37563                 }
37564                 break;
37565             
37566             case e.DOWN:
37567                newCell = walk(r+1, c, 1);
37568                 break;
37569             
37570             case e.UP:
37571                 newCell = walk(r-1, c, -1);
37572                 break;
37573             
37574             case e.RIGHT:
37575                 newCell = walk(r, c+1, 1);
37576                 break;
37577             
37578             case e.LEFT:
37579                 newCell = walk(r, c-1, -1);
37580                 break;
37581             
37582             case e.ENTER:
37583                 
37584                 if(g.isEditor && !g.editing){
37585                    g.startEditing(r, c);
37586                    e.stopEvent();
37587                    return;
37588                 }
37589                 
37590                 
37591              break;
37592         };
37593         if(newCell){
37594             this.select(newCell[0], newCell[1]);
37595             e.stopEvent();
37596             
37597         }
37598     },
37599
37600     acceptsNav : function(row, col, cm){
37601         return !cm.isHidden(col) && cm.isCellEditable(col, row);
37602     },
37603     /**
37604      * Selects a cell.
37605      * @param {Number} field (not used) - as it's normally used as a listener
37606      * @param {Number} e - event - fake it by using
37607      *
37608      * var e = Roo.EventObjectImpl.prototype;
37609      * e.keyCode = e.TAB
37610      *
37611      * 
37612      */
37613     onEditorKey : function(field, e){
37614         
37615         var k = e.getKey(),
37616             newCell,
37617             g = this.grid,
37618             ed = g.activeEditor,
37619             forward = false;
37620         ///Roo.log('onEditorKey' + k);
37621         
37622         
37623         if (this.enter_is_tab && k == e.ENTER) {
37624             k = e.TAB;
37625         }
37626         
37627         if(k == e.TAB){
37628             if(e.shiftKey){
37629                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
37630             }else{
37631                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37632                 forward = true;
37633             }
37634             
37635             e.stopEvent();
37636             
37637         } else if(k == e.ENTER &&  !e.ctrlKey){
37638             ed.completeEdit();
37639             e.stopEvent();
37640             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37641         
37642                 } else if(k == e.ESC){
37643             ed.cancelEdit();
37644         }
37645                 
37646         if (newCell) {
37647             var ecall = { cell : newCell, forward : forward };
37648             this.fireEvent('beforeeditnext', ecall );
37649             newCell = ecall.cell;
37650                         forward = ecall.forward;
37651         }
37652                 
37653         if(newCell){
37654             //Roo.log('next cell after edit');
37655             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
37656         } else if (forward) {
37657             // tabbed past last
37658             this.fireEvent.defer(100, this, ['tabend',this]);
37659         }
37660     }
37661 });/*
37662  * Based on:
37663  * Ext JS Library 1.1.1
37664  * Copyright(c) 2006-2007, Ext JS, LLC.
37665  *
37666  * Originally Released Under LGPL - original licence link has changed is not relivant.
37667  *
37668  * Fork - LGPL
37669  * <script type="text/javascript">
37670  */
37671  
37672 /**
37673  * @class Roo.grid.EditorGrid
37674  * @extends Roo.grid.Grid
37675  * Class for creating and editable grid.
37676  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
37677  * The container MUST have some type of size defined for the grid to fill. The container will be 
37678  * automatically set to position relative if it isn't already.
37679  * @param {Object} dataSource The data model to bind to
37680  * @param {Object} colModel The column model with info about this grid's columns
37681  */
37682 Roo.grid.EditorGrid = function(container, config){
37683     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
37684     this.getGridEl().addClass("xedit-grid");
37685
37686     if(!this.selModel){
37687         this.selModel = new Roo.grid.CellSelectionModel();
37688     }
37689
37690     this.activeEditor = null;
37691
37692         this.addEvents({
37693             /**
37694              * @event beforeedit
37695              * Fires before cell editing is triggered. The edit event object has the following properties <br />
37696              * <ul style="padding:5px;padding-left:16px;">
37697              * <li>grid - This grid</li>
37698              * <li>record - The record being edited</li>
37699              * <li>field - The field name being edited</li>
37700              * <li>value - The value for the field being edited.</li>
37701              * <li>row - The grid row index</li>
37702              * <li>column - The grid column index</li>
37703              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37704              * </ul>
37705              * @param {Object} e An edit event (see above for description)
37706              */
37707             "beforeedit" : true,
37708             /**
37709              * @event afteredit
37710              * Fires after a cell is edited. <br />
37711              * <ul style="padding:5px;padding-left:16px;">
37712              * <li>grid - This grid</li>
37713              * <li>record - The record being edited</li>
37714              * <li>field - The field name being edited</li>
37715              * <li>value - The value being set</li>
37716              * <li>originalValue - The original value for the field, before the edit.</li>
37717              * <li>row - The grid row index</li>
37718              * <li>column - The grid column index</li>
37719              * </ul>
37720              * @param {Object} e An edit event (see above for description)
37721              */
37722             "afteredit" : true,
37723             /**
37724              * @event validateedit
37725              * Fires after a cell is edited, but before the value is set in the record. 
37726          * You can use this to modify the value being set in the field, Return false
37727              * to cancel the change. The edit event object has the following properties <br />
37728              * <ul style="padding:5px;padding-left:16px;">
37729          * <li>editor - This editor</li>
37730              * <li>grid - This grid</li>
37731              * <li>record - The record being edited</li>
37732              * <li>field - The field name being edited</li>
37733              * <li>value - The value being set</li>
37734              * <li>originalValue - The original value for the field, before the edit.</li>
37735              * <li>row - The grid row index</li>
37736              * <li>column - The grid column index</li>
37737              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37738              * </ul>
37739              * @param {Object} e An edit event (see above for description)
37740              */
37741             "validateedit" : true
37742         });
37743     this.on("bodyscroll", this.stopEditing,  this);
37744     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
37745 };
37746
37747 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
37748     /**
37749      * @cfg {Number} clicksToEdit
37750      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
37751      */
37752     clicksToEdit: 2,
37753
37754     // private
37755     isEditor : true,
37756     // private
37757     trackMouseOver: false, // causes very odd FF errors
37758
37759     onCellDblClick : function(g, row, col){
37760         this.startEditing(row, col);
37761     },
37762
37763     onEditComplete : function(ed, value, startValue){
37764         this.editing = false;
37765         this.activeEditor = null;
37766         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
37767         var r = ed.record;
37768         var field = this.colModel.getDataIndex(ed.col);
37769         var e = {
37770             grid: this,
37771             record: r,
37772             field: field,
37773             originalValue: startValue,
37774             value: value,
37775             row: ed.row,
37776             column: ed.col,
37777             cancel:false,
37778             editor: ed
37779         };
37780         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
37781         cell.show();
37782           
37783         if(String(value) !== String(startValue)){
37784             
37785             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
37786                 r.set(field, e.value);
37787                 // if we are dealing with a combo box..
37788                 // then we also set the 'name' colum to be the displayField
37789                 if (ed.field.displayField && ed.field.name) {
37790                     r.set(ed.field.name, ed.field.el.dom.value);
37791                 }
37792                 
37793                 delete e.cancel; //?? why!!!
37794                 this.fireEvent("afteredit", e);
37795             }
37796         } else {
37797             this.fireEvent("afteredit", e); // always fire it!
37798         }
37799         this.view.focusCell(ed.row, ed.col);
37800     },
37801
37802     /**
37803      * Starts editing the specified for the specified row/column
37804      * @param {Number} rowIndex
37805      * @param {Number} colIndex
37806      */
37807     startEditing : function(row, col){
37808         this.stopEditing();
37809         if(this.colModel.isCellEditable(col, row)){
37810             this.view.ensureVisible(row, col, true);
37811           
37812             var r = this.dataSource.getAt(row);
37813             var field = this.colModel.getDataIndex(col);
37814             var cell = Roo.get(this.view.getCell(row,col));
37815             var e = {
37816                 grid: this,
37817                 record: r,
37818                 field: field,
37819                 value: r.data[field],
37820                 row: row,
37821                 column: col,
37822                 cancel:false 
37823             };
37824             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
37825                 this.editing = true;
37826                 var ed = this.colModel.getCellEditor(col, row);
37827                 
37828                 if (!ed) {
37829                     return;
37830                 }
37831                 if(!ed.rendered){
37832                     ed.render(ed.parentEl || document.body);
37833                 }
37834                 ed.field.reset();
37835                
37836                 cell.hide();
37837                 
37838                 (function(){ // complex but required for focus issues in safari, ie and opera
37839                     ed.row = row;
37840                     ed.col = col;
37841                     ed.record = r;
37842                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
37843                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
37844                     this.activeEditor = ed;
37845                     var v = r.data[field];
37846                     ed.startEdit(this.view.getCell(row, col), v);
37847                     // combo's with 'displayField and name set
37848                     if (ed.field.displayField && ed.field.name) {
37849                         ed.field.el.dom.value = r.data[ed.field.name];
37850                     }
37851                     
37852                     
37853                 }).defer(50, this);
37854             }
37855         }
37856     },
37857         
37858     /**
37859      * Stops any active editing
37860      */
37861     stopEditing : function(){
37862         if(this.activeEditor){
37863             this.activeEditor.completeEdit();
37864         }
37865         this.activeEditor = null;
37866     }
37867 });/*
37868  * Based on:
37869  * Ext JS Library 1.1.1
37870  * Copyright(c) 2006-2007, Ext JS, LLC.
37871  *
37872  * Originally Released Under LGPL - original licence link has changed is not relivant.
37873  *
37874  * Fork - LGPL
37875  * <script type="text/javascript">
37876  */
37877
37878 // private - not really -- you end up using it !
37879 // This is a support class used internally by the Grid components
37880
37881 /**
37882  * @class Roo.grid.GridEditor
37883  * @extends Roo.Editor
37884  * Class for creating and editable grid elements.
37885  * @param {Object} config any settings (must include field)
37886  */
37887 Roo.grid.GridEditor = function(field, config){
37888     if (!config && field.field) {
37889         config = field;
37890         field = Roo.factory(config.field, Roo.form);
37891     }
37892     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
37893     field.monitorTab = false;
37894 };
37895
37896 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
37897     
37898     /**
37899      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
37900      */
37901     
37902     alignment: "tl-tl",
37903     autoSize: "width",
37904     hideEl : false,
37905     cls: "x-small-editor x-grid-editor",
37906     shim:false,
37907     shadow:"frame"
37908 });/*
37909  * Based on:
37910  * Ext JS Library 1.1.1
37911  * Copyright(c) 2006-2007, Ext JS, LLC.
37912  *
37913  * Originally Released Under LGPL - original licence link has changed is not relivant.
37914  *
37915  * Fork - LGPL
37916  * <script type="text/javascript">
37917  */
37918   
37919
37920   
37921 Roo.grid.PropertyRecord = Roo.data.Record.create([
37922     {name:'name',type:'string'},  'value'
37923 ]);
37924
37925
37926 Roo.grid.PropertyStore = function(grid, source){
37927     this.grid = grid;
37928     this.store = new Roo.data.Store({
37929         recordType : Roo.grid.PropertyRecord
37930     });
37931     this.store.on('update', this.onUpdate,  this);
37932     if(source){
37933         this.setSource(source);
37934     }
37935     Roo.grid.PropertyStore.superclass.constructor.call(this);
37936 };
37937
37938
37939
37940 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
37941     setSource : function(o){
37942         this.source = o;
37943         this.store.removeAll();
37944         var data = [];
37945         for(var k in o){
37946             if(this.isEditableValue(o[k])){
37947                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
37948             }
37949         }
37950         this.store.loadRecords({records: data}, {}, true);
37951     },
37952
37953     onUpdate : function(ds, record, type){
37954         if(type == Roo.data.Record.EDIT){
37955             var v = record.data['value'];
37956             var oldValue = record.modified['value'];
37957             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
37958                 this.source[record.id] = v;
37959                 record.commit();
37960                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
37961             }else{
37962                 record.reject();
37963             }
37964         }
37965     },
37966
37967     getProperty : function(row){
37968        return this.store.getAt(row);
37969     },
37970
37971     isEditableValue: function(val){
37972         if(val && val instanceof Date){
37973             return true;
37974         }else if(typeof val == 'object' || typeof val == 'function'){
37975             return false;
37976         }
37977         return true;
37978     },
37979
37980     setValue : function(prop, value){
37981         this.source[prop] = value;
37982         this.store.getById(prop).set('value', value);
37983     },
37984
37985     getSource : function(){
37986         return this.source;
37987     }
37988 });
37989
37990 Roo.grid.PropertyColumnModel = function(grid, store){
37991     this.grid = grid;
37992     var g = Roo.grid;
37993     g.PropertyColumnModel.superclass.constructor.call(this, [
37994         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37995         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37996     ]);
37997     this.store = store;
37998     this.bselect = Roo.DomHelper.append(document.body, {
37999         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
38000             {tag: 'option', value: 'true', html: 'true'},
38001             {tag: 'option', value: 'false', html: 'false'}
38002         ]
38003     });
38004     Roo.id(this.bselect);
38005     var f = Roo.form;
38006     this.editors = {
38007         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
38008         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
38009         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
38010         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
38011         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
38012     };
38013     this.renderCellDelegate = this.renderCell.createDelegate(this);
38014     this.renderPropDelegate = this.renderProp.createDelegate(this);
38015 };
38016
38017 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
38018     
38019     
38020     nameText : 'Name',
38021     valueText : 'Value',
38022     
38023     dateFormat : 'm/j/Y',
38024     
38025     
38026     renderDate : function(dateVal){
38027         return dateVal.dateFormat(this.dateFormat);
38028     },
38029
38030     renderBool : function(bVal){
38031         return bVal ? 'true' : 'false';
38032     },
38033
38034     isCellEditable : function(colIndex, rowIndex){
38035         return colIndex == 1;
38036     },
38037
38038     getRenderer : function(col){
38039         return col == 1 ?
38040             this.renderCellDelegate : this.renderPropDelegate;
38041     },
38042
38043     renderProp : function(v){
38044         return this.getPropertyName(v);
38045     },
38046
38047     renderCell : function(val){
38048         var rv = val;
38049         if(val instanceof Date){
38050             rv = this.renderDate(val);
38051         }else if(typeof val == 'boolean'){
38052             rv = this.renderBool(val);
38053         }
38054         return Roo.util.Format.htmlEncode(rv);
38055     },
38056
38057     getPropertyName : function(name){
38058         var pn = this.grid.propertyNames;
38059         return pn && pn[name] ? pn[name] : name;
38060     },
38061
38062     getCellEditor : function(colIndex, rowIndex){
38063         var p = this.store.getProperty(rowIndex);
38064         var n = p.data['name'], val = p.data['value'];
38065         
38066         if(typeof(this.grid.customEditors[n]) == 'string'){
38067             return this.editors[this.grid.customEditors[n]];
38068         }
38069         if(typeof(this.grid.customEditors[n]) != 'undefined'){
38070             return this.grid.customEditors[n];
38071         }
38072         if(val instanceof Date){
38073             return this.editors['date'];
38074         }else if(typeof val == 'number'){
38075             return this.editors['number'];
38076         }else if(typeof val == 'boolean'){
38077             return this.editors['boolean'];
38078         }else{
38079             return this.editors['string'];
38080         }
38081     }
38082 });
38083
38084 /**
38085  * @class Roo.grid.PropertyGrid
38086  * @extends Roo.grid.EditorGrid
38087  * This class represents the  interface of a component based property grid control.
38088  * <br><br>Usage:<pre><code>
38089  var grid = new Roo.grid.PropertyGrid("my-container-id", {
38090       
38091  });
38092  // set any options
38093  grid.render();
38094  * </code></pre>
38095   
38096  * @constructor
38097  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
38098  * The container MUST have some type of size defined for the grid to fill. The container will be
38099  * automatically set to position relative if it isn't already.
38100  * @param {Object} config A config object that sets properties on this grid.
38101  */
38102 Roo.grid.PropertyGrid = function(container, config){
38103     config = config || {};
38104     var store = new Roo.grid.PropertyStore(this);
38105     this.store = store;
38106     var cm = new Roo.grid.PropertyColumnModel(this, store);
38107     store.store.sort('name', 'ASC');
38108     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
38109         ds: store.store,
38110         cm: cm,
38111         enableColLock:false,
38112         enableColumnMove:false,
38113         stripeRows:false,
38114         trackMouseOver: false,
38115         clicksToEdit:1
38116     }, config));
38117     this.getGridEl().addClass('x-props-grid');
38118     this.lastEditRow = null;
38119     this.on('columnresize', this.onColumnResize, this);
38120     this.addEvents({
38121          /**
38122              * @event beforepropertychange
38123              * Fires before a property changes (return false to stop?)
38124              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38125              * @param {String} id Record Id
38126              * @param {String} newval New Value
38127          * @param {String} oldval Old Value
38128              */
38129         "beforepropertychange": true,
38130         /**
38131              * @event propertychange
38132              * Fires after a property changes
38133              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38134              * @param {String} id Record Id
38135              * @param {String} newval New Value
38136          * @param {String} oldval Old Value
38137              */
38138         "propertychange": true
38139     });
38140     this.customEditors = this.customEditors || {};
38141 };
38142 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
38143     
38144      /**
38145      * @cfg {Object} customEditors map of colnames=> custom editors.
38146      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
38147      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
38148      * false disables editing of the field.
38149          */
38150     
38151       /**
38152      * @cfg {Object} propertyNames map of property Names to their displayed value
38153          */
38154     
38155     render : function(){
38156         Roo.grid.PropertyGrid.superclass.render.call(this);
38157         this.autoSize.defer(100, this);
38158     },
38159
38160     autoSize : function(){
38161         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
38162         if(this.view){
38163             this.view.fitColumns();
38164         }
38165     },
38166
38167     onColumnResize : function(){
38168         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
38169         this.autoSize();
38170     },
38171     /**
38172      * Sets the data for the Grid
38173      * accepts a Key => Value object of all the elements avaiable.
38174      * @param {Object} data  to appear in grid.
38175      */
38176     setSource : function(source){
38177         this.store.setSource(source);
38178         //this.autoSize();
38179     },
38180     /**
38181      * Gets all the data from the grid.
38182      * @return {Object} data  data stored in grid
38183      */
38184     getSource : function(){
38185         return this.store.getSource();
38186     }
38187 });/*
38188  * Based on:
38189  * Ext JS Library 1.1.1
38190  * Copyright(c) 2006-2007, Ext JS, LLC.
38191  *
38192  * Originally Released Under LGPL - original licence link has changed is not relivant.
38193  *
38194  * Fork - LGPL
38195  * <script type="text/javascript">
38196  */
38197  
38198 /**
38199  * @class Roo.LoadMask
38200  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38201  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38202  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38203  * element's UpdateManager load indicator and will be destroyed after the initial load.
38204  * @constructor
38205  * Create a new LoadMask
38206  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38207  * @param {Object} config The config object
38208  */
38209 Roo.LoadMask = function(el, config){
38210     this.el = Roo.get(el);
38211     Roo.apply(this, config);
38212     if(this.store){
38213         this.store.on('beforeload', this.onBeforeLoad, this);
38214         this.store.on('load', this.onLoad, this);
38215         this.store.on('loadexception', this.onLoadException, this);
38216         this.removeMask = false;
38217     }else{
38218         var um = this.el.getUpdateManager();
38219         um.showLoadIndicator = false; // disable the default indicator
38220         um.on('beforeupdate', this.onBeforeLoad, this);
38221         um.on('update', this.onLoad, this);
38222         um.on('failure', this.onLoad, this);
38223         this.removeMask = true;
38224     }
38225 };
38226
38227 Roo.LoadMask.prototype = {
38228     /**
38229      * @cfg {Boolean} removeMask
38230      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38231      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38232      */
38233     /**
38234      * @cfg {String} msg
38235      * The text to display in a centered loading message box (defaults to 'Loading...')
38236      */
38237     msg : 'Loading...',
38238     /**
38239      * @cfg {String} msgCls
38240      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38241      */
38242     msgCls : 'x-mask-loading',
38243
38244     /**
38245      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38246      * @type Boolean
38247      */
38248     disabled: false,
38249
38250     /**
38251      * Disables the mask to prevent it from being displayed
38252      */
38253     disable : function(){
38254        this.disabled = true;
38255     },
38256
38257     /**
38258      * Enables the mask so that it can be displayed
38259      */
38260     enable : function(){
38261         this.disabled = false;
38262     },
38263     
38264     onLoadException : function()
38265     {
38266         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38267             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38268         }
38269         this.el.unmask(this.removeMask);
38270     },
38271     // private
38272     onLoad : function()
38273     {
38274         this.el.unmask(this.removeMask);
38275     },
38276
38277     // private
38278     onBeforeLoad : function(){
38279         if(!this.disabled){
38280             this.el.mask(this.msg, this.msgCls);
38281         }
38282     },
38283
38284     // private
38285     destroy : function(){
38286         if(this.store){
38287             this.store.un('beforeload', this.onBeforeLoad, this);
38288             this.store.un('load', this.onLoad, this);
38289             this.store.un('loadexception', this.onLoadException, this);
38290         }else{
38291             var um = this.el.getUpdateManager();
38292             um.un('beforeupdate', this.onBeforeLoad, this);
38293             um.un('update', this.onLoad, this);
38294             um.un('failure', this.onLoad, this);
38295         }
38296     }
38297 };/*
38298  * Based on:
38299  * Ext JS Library 1.1.1
38300  * Copyright(c) 2006-2007, Ext JS, LLC.
38301  *
38302  * Originally Released Under LGPL - original licence link has changed is not relivant.
38303  *
38304  * Fork - LGPL
38305  * <script type="text/javascript">
38306  */
38307
38308
38309 /**
38310  * @class Roo.XTemplate
38311  * @extends Roo.Template
38312  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38313 <pre><code>
38314 var t = new Roo.XTemplate(
38315         '&lt;select name="{name}"&gt;',
38316                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38317         '&lt;/select&gt;'
38318 );
38319  
38320 // then append, applying the master template values
38321  </code></pre>
38322  *
38323  * Supported features:
38324  *
38325  *  Tags:
38326
38327 <pre><code>
38328       {a_variable} - output encoded.
38329       {a_variable.format:("Y-m-d")} - call a method on the variable
38330       {a_variable:raw} - unencoded output
38331       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38332       {a_variable:this.method_on_template(...)} - call a method on the template object.
38333  
38334 </code></pre>
38335  *  The tpl tag:
38336 <pre><code>
38337         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38338         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38339         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38340         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38341   
38342         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38343         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38344 </code></pre>
38345  *      
38346  */
38347 Roo.XTemplate = function()
38348 {
38349     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38350     if (this.html) {
38351         this.compile();
38352     }
38353 };
38354
38355
38356 Roo.extend(Roo.XTemplate, Roo.Template, {
38357
38358     /**
38359      * The various sub templates
38360      */
38361     tpls : false,
38362     /**
38363      *
38364      * basic tag replacing syntax
38365      * WORD:WORD()
38366      *
38367      * // you can fake an object call by doing this
38368      *  x.t:(test,tesT) 
38369      * 
38370      */
38371     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38372
38373     /**
38374      * compile the template
38375      *
38376      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38377      *
38378      */
38379     compile: function()
38380     {
38381         var s = this.html;
38382      
38383         s = ['<tpl>', s, '</tpl>'].join('');
38384     
38385         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38386             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38387             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38388             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38389             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38390             m,
38391             id     = 0,
38392             tpls   = [];
38393     
38394         while(true == !!(m = s.match(re))){
38395             var forMatch   = m[0].match(nameRe),
38396                 ifMatch   = m[0].match(ifRe),
38397                 execMatch   = m[0].match(execRe),
38398                 namedMatch   = m[0].match(namedRe),
38399                 
38400                 exp  = null, 
38401                 fn   = null,
38402                 exec = null,
38403                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38404                 
38405             if (ifMatch) {
38406                 // if - puts fn into test..
38407                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38408                 if(exp){
38409                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38410                 }
38411             }
38412             
38413             if (execMatch) {
38414                 // exec - calls a function... returns empty if true is  returned.
38415                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38416                 if(exp){
38417                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38418                 }
38419             }
38420             
38421             
38422             if (name) {
38423                 // for = 
38424                 switch(name){
38425                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38426                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38427                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38428                 }
38429             }
38430             var uid = namedMatch ? namedMatch[1] : id;
38431             
38432             
38433             tpls.push({
38434                 id:     namedMatch ? namedMatch[1] : id,
38435                 target: name,
38436                 exec:   exec,
38437                 test:   fn,
38438                 body:   m[1] || ''
38439             });
38440             if (namedMatch) {
38441                 s = s.replace(m[0], '');
38442             } else { 
38443                 s = s.replace(m[0], '{xtpl'+ id + '}');
38444             }
38445             ++id;
38446         }
38447         this.tpls = [];
38448         for(var i = tpls.length-1; i >= 0; --i){
38449             this.compileTpl(tpls[i]);
38450             this.tpls[tpls[i].id] = tpls[i];
38451         }
38452         this.master = tpls[tpls.length-1];
38453         return this;
38454     },
38455     /**
38456      * same as applyTemplate, except it's done to one of the subTemplates
38457      * when using named templates, you can do:
38458      *
38459      * var str = pl.applySubTemplate('your-name', values);
38460      *
38461      * 
38462      * @param {Number} id of the template
38463      * @param {Object} values to apply to template
38464      * @param {Object} parent (normaly the instance of this object)
38465      */
38466     applySubTemplate : function(id, values, parent)
38467     {
38468         
38469         
38470         var t = this.tpls[id];
38471         
38472         
38473         try { 
38474             if(t.test && !t.test.call(this, values, parent)){
38475                 return '';
38476             }
38477         } catch(e) {
38478             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38479             Roo.log(e.toString());
38480             Roo.log(t.test);
38481             return ''
38482         }
38483         try { 
38484             
38485             if(t.exec && t.exec.call(this, values, parent)){
38486                 return '';
38487             }
38488         } catch(e) {
38489             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38490             Roo.log(e.toString());
38491             Roo.log(t.exec);
38492             return ''
38493         }
38494         try {
38495             var vs = t.target ? t.target.call(this, values, parent) : values;
38496             parent = t.target ? values : parent;
38497             if(t.target && vs instanceof Array){
38498                 var buf = [];
38499                 for(var i = 0, len = vs.length; i < len; i++){
38500                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38501                 }
38502                 return buf.join('');
38503             }
38504             return t.compiled.call(this, vs, parent);
38505         } catch (e) {
38506             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38507             Roo.log(e.toString());
38508             Roo.log(t.compiled);
38509             return '';
38510         }
38511     },
38512
38513     compileTpl : function(tpl)
38514     {
38515         var fm = Roo.util.Format;
38516         var useF = this.disableFormats !== true;
38517         var sep = Roo.isGecko ? "+" : ",";
38518         var undef = function(str) {
38519             Roo.log("Property not found :"  + str);
38520             return '';
38521         };
38522         
38523         var fn = function(m, name, format, args)
38524         {
38525             //Roo.log(arguments);
38526             args = args ? args.replace(/\\'/g,"'") : args;
38527             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38528             if (typeof(format) == 'undefined') {
38529                 format= 'htmlEncode';
38530             }
38531             if (format == 'raw' ) {
38532                 format = false;
38533             }
38534             
38535             if(name.substr(0, 4) == 'xtpl'){
38536                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38537             }
38538             
38539             // build an array of options to determine if value is undefined..
38540             
38541             // basically get 'xxxx.yyyy' then do
38542             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38543             //    (function () { Roo.log("Property not found"); return ''; })() :
38544             //    ......
38545             
38546             var udef_ar = [];
38547             var lookfor = '';
38548             Roo.each(name.split('.'), function(st) {
38549                 lookfor += (lookfor.length ? '.': '') + st;
38550                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38551             });
38552             
38553             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38554             
38555             
38556             if(format && useF){
38557                 
38558                 args = args ? ',' + args : "";
38559                  
38560                 if(format.substr(0, 5) != "this."){
38561                     format = "fm." + format + '(';
38562                 }else{
38563                     format = 'this.call("'+ format.substr(5) + '", ';
38564                     args = ", values";
38565                 }
38566                 
38567                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38568             }
38569              
38570             if (args.length) {
38571                 // called with xxyx.yuu:(test,test)
38572                 // change to ()
38573                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38574             }
38575             // raw.. - :raw modifier..
38576             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38577             
38578         };
38579         var body;
38580         // branched to use + in gecko and [].join() in others
38581         if(Roo.isGecko){
38582             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38583                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38584                     "';};};";
38585         }else{
38586             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38587             body.push(tpl.body.replace(/(\r\n|\n)/g,
38588                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38589             body.push("'].join('');};};");
38590             body = body.join('');
38591         }
38592         
38593         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38594        
38595         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38596         eval(body);
38597         
38598         return this;
38599     },
38600
38601     applyTemplate : function(values){
38602         return this.master.compiled.call(this, values, {});
38603         //var s = this.subs;
38604     },
38605
38606     apply : function(){
38607         return this.applyTemplate.apply(this, arguments);
38608     }
38609
38610  });
38611
38612 Roo.XTemplate.from = function(el){
38613     el = Roo.getDom(el);
38614     return new Roo.XTemplate(el.value || el.innerHTML);
38615 };/*
38616  * Original code for Roojs - LGPL
38617  * <script type="text/javascript">
38618  */
38619  
38620 /**
38621  * @class Roo.XComponent
38622  * A delayed Element creator...
38623  * Or a way to group chunks of interface together.
38624  * 
38625  * Mypart.xyx = new Roo.XComponent({
38626
38627     parent : 'Mypart.xyz', // empty == document.element.!!
38628     order : '001',
38629     name : 'xxxx'
38630     region : 'xxxx'
38631     disabled : function() {} 
38632      
38633     tree : function() { // return an tree of xtype declared components
38634         var MODULE = this;
38635         return 
38636         {
38637             xtype : 'NestedLayoutPanel',
38638             // technicall
38639         }
38640      ]
38641  *})
38642  *
38643  *
38644  * It can be used to build a big heiracy, with parent etc.
38645  * or you can just use this to render a single compoent to a dom element
38646  * MYPART.render(Roo.Element | String(id) | dom_element )
38647  * 
38648  * @extends Roo.util.Observable
38649  * @constructor
38650  * @param cfg {Object} configuration of component
38651  * 
38652  */
38653 Roo.XComponent = function(cfg) {
38654     Roo.apply(this, cfg);
38655     this.addEvents({ 
38656         /**
38657              * @event built
38658              * Fires when this the componnt is built
38659              * @param {Roo.XComponent} c the component
38660              */
38661         'built' : true
38662         
38663     });
38664     this.region = this.region || 'center'; // default..
38665     Roo.XComponent.register(this);
38666     this.modules = false;
38667     this.el = false; // where the layout goes..
38668     
38669     
38670 }
38671 Roo.extend(Roo.XComponent, Roo.util.Observable, {
38672     /**
38673      * @property el
38674      * The created element (with Roo.factory())
38675      * @type {Roo.Layout}
38676      */
38677     el  : false,
38678     
38679     /**
38680      * @property el
38681      * for BC  - use el in new code
38682      * @type {Roo.Layout}
38683      */
38684     panel : false,
38685     
38686     /**
38687      * @property layout
38688      * for BC  - use el in new code
38689      * @type {Roo.Layout}
38690      */
38691     layout : false,
38692     
38693      /**
38694      * @cfg {Function|boolean} disabled
38695      * If this module is disabled by some rule, return true from the funtion
38696      */
38697     disabled : false,
38698     
38699     /**
38700      * @cfg {String} parent 
38701      * Name of parent element which it get xtype added to..
38702      */
38703     parent: false,
38704     
38705     /**
38706      * @cfg {String} order
38707      * Used to set the order in which elements are created (usefull for multiple tabs)
38708      */
38709     
38710     order : false,
38711     /**
38712      * @cfg {String} name
38713      * String to display while loading.
38714      */
38715     name : false,
38716     /**
38717      * @cfg {String} region
38718      * Region to render component to (defaults to center)
38719      */
38720     region : 'center',
38721     
38722     /**
38723      * @cfg {Array} items
38724      * A single item array - the first element is the root of the tree..
38725      * It's done this way to stay compatible with the Xtype system...
38726      */
38727     items : false,
38728     
38729     /**
38730      * @property _tree
38731      * The method that retuns the tree of parts that make up this compoennt 
38732      * @type {function}
38733      */
38734     _tree  : false,
38735     
38736      /**
38737      * render
38738      * render element to dom or tree
38739      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
38740      */
38741     
38742     render : function(el)
38743     {
38744         
38745         el = el || false;
38746         var hp = this.parent ? 1 : 0;
38747         
38748         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
38749             // if parent is a '#.....' string, then let's use that..
38750             var ename = this.parent.substr(1)
38751             this.parent = false;
38752             el = Roo.get(ename);
38753             if (!el) {
38754                 Roo.log("Warning - element can not be found :#" + ename );
38755                 return;
38756             }
38757         }
38758         
38759         
38760         if (!this.parent) {
38761             
38762             el = el ? Roo.get(el) : false;      
38763             
38764             // it's a top level one..
38765             this.parent =  {
38766                 el : new Roo.BorderLayout(el || document.body, {
38767                 
38768                      center: {
38769                          titlebar: false,
38770                          autoScroll:false,
38771                          closeOnTab: true,
38772                          tabPosition: 'top',
38773                           //resizeTabs: true,
38774                          alwaysShowTabs: el && hp? false :  true,
38775                          hideTabs: el || !hp ? true :  false,
38776                          minTabWidth: 140
38777                      }
38778                  })
38779             }
38780         }
38781         
38782                 if (!this.parent.el) {
38783                         // probably an old style ctor, which has been disabled.
38784                         return;
38785                         
38786                 }
38787                 // The 'tree' method is  '_tree now' 
38788             
38789         var tree = this._tree ? this._tree() : this.tree();
38790         tree.region = tree.region || this.region;
38791         this.el = this.parent.el.addxtype(tree);
38792         this.fireEvent('built', this);
38793         
38794         this.panel = this.el;
38795         this.layout = this.panel.layout;
38796                 this.parentLayout = this.parent.layout  || false;  
38797          
38798     }
38799     
38800 });
38801
38802 Roo.apply(Roo.XComponent, {
38803     /**
38804      * @property  hideProgress
38805      * true to disable the building progress bar.. usefull on single page renders.
38806      * @type Boolean
38807      */
38808     hideProgress : false,
38809     /**
38810      * @property  buildCompleted
38811      * True when the builder has completed building the interface.
38812      * @type Boolean
38813      */
38814     buildCompleted : false,
38815      
38816     /**
38817      * @property  topModule
38818      * the upper most module - uses document.element as it's constructor.
38819      * @type Object
38820      */
38821      
38822     topModule  : false,
38823       
38824     /**
38825      * @property  modules
38826      * array of modules to be created by registration system.
38827      * @type {Array} of Roo.XComponent
38828      */
38829     
38830     modules : [],
38831     /**
38832      * @property  elmodules
38833      * array of modules to be created by which use #ID 
38834      * @type {Array} of Roo.XComponent
38835      */
38836      
38837     elmodules : [],
38838
38839     
38840     /**
38841      * Register components to be built later.
38842      *
38843      * This solves the following issues
38844      * - Building is not done on page load, but after an authentication process has occured.
38845      * - Interface elements are registered on page load
38846      * - Parent Interface elements may not be loaded before child, so this handles that..
38847      * 
38848      *
38849      * example:
38850      * 
38851      * MyApp.register({
38852           order : '000001',
38853           module : 'Pman.Tab.projectMgr',
38854           region : 'center',
38855           parent : 'Pman.layout',
38856           disabled : false,  // or use a function..
38857         })
38858      
38859      * * @param {Object} details about module
38860      */
38861     register : function(obj) {
38862                 
38863         Roo.XComponent.event.fireEvent('register', obj);
38864         switch(typeof(obj.disabled) ) {
38865                 
38866             case 'undefined':
38867                 break;
38868             
38869             case 'function':
38870                 if ( obj.disabled() ) {
38871                         return;
38872                 }
38873                 break;
38874             
38875             default:
38876                 if (obj.disabled) {
38877                         return;
38878                 }
38879                 break;
38880         }
38881                 
38882         this.modules.push(obj);
38883          
38884     },
38885     /**
38886      * convert a string to an object..
38887      * eg. 'AAA.BBB' -> finds AAA.BBB
38888
38889      */
38890     
38891     toObject : function(str)
38892     {
38893         if (!str || typeof(str) == 'object') {
38894             return str;
38895         }
38896         if (str.substring(0,1) == '#') {
38897             return str;
38898         }
38899
38900         var ar = str.split('.');
38901         var rt, o;
38902         rt = ar.shift();
38903             /** eval:var:o */
38904         try {
38905             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
38906         } catch (e) {
38907             throw "Module not found : " + str;
38908         }
38909         
38910         if (o === false) {
38911             throw "Module not found : " + str;
38912         }
38913         Roo.each(ar, function(e) {
38914             if (typeof(o[e]) == 'undefined') {
38915                 throw "Module not found : " + str;
38916             }
38917             o = o[e];
38918         });
38919         
38920         return o;
38921         
38922     },
38923     
38924     
38925     /**
38926      * move modules into their correct place in the tree..
38927      * 
38928      */
38929     preBuild : function ()
38930     {
38931         var _t = this;
38932         Roo.each(this.modules , function (obj)
38933         {
38934             Roo.XComponent.event.fireEvent('beforebuild', obj);
38935             
38936             var opar = obj.parent;
38937             try { 
38938                 obj.parent = this.toObject(opar);
38939             } catch(e) {
38940                 Roo.log("parent:toObject failed: " + e.toString());
38941                 return;
38942             }
38943             
38944             if (!obj.parent) {
38945                 Roo.debug && Roo.log("GOT top level module");
38946                 Roo.debug && Roo.log(obj);
38947                 obj.modules = new Roo.util.MixedCollection(false, 
38948                     function(o) { return o.order + '' }
38949                 );
38950                 this.topModule = obj;
38951                 return;
38952             }
38953                         // parent is a string (usually a dom element name..)
38954             if (typeof(obj.parent) == 'string') {
38955                 this.elmodules.push(obj);
38956                 return;
38957             }
38958             if (obj.parent.constructor != Roo.XComponent) {
38959                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
38960             }
38961             if (!obj.parent.modules) {
38962                 obj.parent.modules = new Roo.util.MixedCollection(false, 
38963                     function(o) { return o.order + '' }
38964                 );
38965             }
38966             if (obj.parent.disabled) {
38967                 obj.disabled = true;
38968             }
38969             obj.parent.modules.add(obj);
38970         }, this);
38971     },
38972     
38973      /**
38974      * make a list of modules to build.
38975      * @return {Array} list of modules. 
38976      */ 
38977     
38978     buildOrder : function()
38979     {
38980         var _this = this;
38981         var cmp = function(a,b) {   
38982             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
38983         };
38984         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
38985             throw "No top level modules to build";
38986         }
38987         
38988         // make a flat list in order of modules to build.
38989         var mods = this.topModule ? [ this.topModule ] : [];
38990                 
38991         // elmodules (is a list of DOM based modules )
38992         Roo.each(this.elmodules, function(e) {
38993             mods.push(e)
38994         });
38995
38996         
38997         // add modules to their parents..
38998         var addMod = function(m) {
38999             Roo.debug && Roo.log("build Order: add: " + m.name);
39000             
39001         mods.push(m);
39002         if (m.modules && !m.disabled) {
39003             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
39004             m.modules.keySort('ASC',  cmp );
39005             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
39006
39007             m.modules.each(addMod);
39008         } else {
39009             Roo.debug && Roo.log("build Order: no child modules");
39010             }
39011             // not sure if this is used any more..
39012             if (m.finalize) {
39013                 m.finalize.name = m.name + " (clean up) ";
39014                 mods.push(m.finalize);
39015             }
39016             
39017         }
39018         if (this.topModule) { 
39019             this.topModule.modules.keySort('ASC',  cmp );
39020             this.topModule.modules.each(addMod);
39021         }
39022         return mods;
39023     },
39024     
39025      /**
39026      * Build the registered modules.
39027      * @param {Object} parent element.
39028      * @param {Function} optional method to call after module has been added.
39029      * 
39030      */ 
39031    
39032     build : function() 
39033     {
39034         
39035         this.preBuild();
39036         var mods = this.buildOrder();
39037       
39038         //this.allmods = mods;
39039         //Roo.debug && Roo.log(mods);
39040         //return;
39041         if (!mods.length) { // should not happen
39042             throw "NO modules!!!";
39043         }
39044         
39045         
39046         var msg = "Building Interface...";
39047         // flash it up as modal - so we store the mask!?
39048         if (!this.hideProgress) {
39049             Roo.MessageBox.show({ title: 'loading' });
39050             Roo.MessageBox.show({
39051                title: "Please wait...",
39052                msg: msg,
39053                width:450,
39054                progress:true,
39055                closable:false,
39056                modal: false
39057               
39058             });
39059         }
39060         var total = mods.length;
39061         
39062         var _this = this;
39063         var progressRun = function() {
39064             if (!mods.length) {
39065                 Roo.debug && Roo.log('hide?');
39066                 if (!this.hideProgress) {
39067                     Roo.MessageBox.hide();
39068                 }
39069                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
39070                 
39071                 // THE END...
39072                 return false;   
39073             }
39074             
39075             var m = mods.shift();
39076             
39077             
39078             Roo.debug && Roo.log(m);
39079             // not sure if this is supported any more.. - modules that are are just function
39080             if (typeof(m) == 'function') { 
39081                 m.call(this);
39082                 return progressRun.defer(10, _this);
39083             } 
39084             
39085             
39086             msg = "Building Interface " + (total  - mods.length) + 
39087                     " of " + total + 
39088                     (m.name ? (' - ' + m.name) : '');
39089                         Roo.debug && Roo.log(msg);
39090             if (!this.hideProgress) { 
39091                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
39092             }
39093             
39094          
39095             // is the module disabled?
39096             var disabled = (typeof(m.disabled) == 'function') ?
39097                 m.disabled.call(m.module.disabled) : m.disabled;    
39098             
39099             
39100             if (disabled) {
39101                 return progressRun(); // we do not update the display!
39102             }
39103             
39104             // now build 
39105             
39106                         
39107                         
39108             m.render();
39109             // it's 10 on top level, and 1 on others??? why...
39110             return progressRun.defer(10, _this);
39111              
39112         }
39113         progressRun.defer(1, _this);
39114      
39115         
39116         
39117     },
39118         
39119         
39120         /**
39121          * Event Object.
39122          *
39123          *
39124          */
39125         event: false, 
39126     /**
39127          * wrapper for event.on - aliased later..  
39128          * Typically use to register a event handler for register:
39129          *
39130          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
39131          *
39132          */
39133     on : false
39134    
39135     
39136     
39137 });
39138
39139 Roo.XComponent.event = new Roo.util.Observable({
39140                 events : { 
39141                         /**
39142                          * @event register
39143                          * Fires when an Component is registered,
39144                          * set the disable property on the Component to stop registration.
39145                          * @param {Roo.XComponent} c the component being registerd.
39146                          * 
39147                          */
39148                         'register' : true,
39149             /**
39150                          * @event beforebuild
39151                          * Fires before each Component is built
39152                          * can be used to apply permissions.
39153                          * @param {Roo.XComponent} c the component being registerd.
39154                          * 
39155                          */
39156                         'beforebuild' : true,
39157                         /**
39158                          * @event buildcomplete
39159                          * Fires on the top level element when all elements have been built
39160                          * @param {Roo.XComponent} the top level component.
39161                          */
39162                         'buildcomplete' : true
39163                         
39164                 }
39165 });
39166
39167 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
39168  //<script type="text/javascript">
39169
39170
39171 /**
39172  * @class Roo.Login
39173  * @extends Roo.LayoutDialog
39174  * A generic Login Dialog..... - only one needed in theory!?!?
39175  *
39176  * Fires XComponent builder on success...
39177  * 
39178  * Sends 
39179  *    username,password, lang = for login actions.
39180  *    check = 1 for periodic checking that sesion is valid.
39181  *    passwordRequest = email request password
39182  *    logout = 1 = to logout
39183  * 
39184  * Affects: (this id="????" elements)
39185  *   loading  (removed) (used to indicate application is loading)
39186  *   loading-mask (hides) (used to hide application when it's building loading)
39187  *   
39188  * 
39189  * Usage: 
39190  *    
39191  * 
39192  * Myapp.login = Roo.Login({
39193      url: xxxx,
39194    
39195      realm : 'Myapp', 
39196      
39197      
39198      method : 'POST',
39199      
39200      
39201      * 
39202  })
39203  * 
39204  * 
39205  * 
39206  **/
39207  
39208 Roo.Login = function(cfg)
39209 {
39210     this.addEvents({
39211         'refreshed' : true
39212     });
39213     
39214     Roo.apply(this,cfg);
39215     
39216     Roo.onReady(function() {
39217         this.onLoad();
39218     }, this);
39219     // call parent..
39220     
39221    
39222     Roo.Login.superclass.constructor.call(this, this);
39223     //this.addxtype(this.items[0]);
39224     
39225     
39226 }
39227
39228
39229 Roo.extend(Roo.Login, Roo.LayoutDialog, {
39230     
39231     /**
39232      * @cfg {String} method
39233      * Method used to query for login details.
39234      */
39235     
39236     method : 'POST',
39237     /**
39238      * @cfg {String} url
39239      * URL to query login data. - eg. baseURL + '/Login.php'
39240      */
39241     url : '',
39242     
39243     /**
39244      * @property user
39245      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
39246      * @type {Object} 
39247      */
39248     user : false,
39249     /**
39250      * @property checkFails
39251      * Number of times we have attempted to get authentication check, and failed.
39252      * @type {Number} 
39253      */
39254     checkFails : 0,
39255       /**
39256      * @property intervalID
39257      * The window interval that does the constant login checking.
39258      * @type {Number} 
39259      */
39260     intervalID : 0,
39261     
39262     
39263     onLoad : function() // called on page load...
39264     {
39265         // load 
39266          
39267         if (Roo.get('loading')) { // clear any loading indicator..
39268             Roo.get('loading').remove();
39269         }
39270         
39271         //this.switchLang('en'); // set the language to english..
39272        
39273         this.check({
39274             success:  function(response, opts)  {  // check successfull...
39275             
39276                 var res = this.processResponse(response);
39277                 this.checkFails =0;
39278                 if (!res.success) { // error!
39279                     this.checkFails = 5;
39280                     //console.log('call failure');
39281                     return this.failure(response,opts);
39282                 }
39283                 
39284                 if (!res.data.id) { // id=0 == login failure.
39285                     return this.show();
39286                 }
39287                 
39288                               
39289                         //console.log(success);
39290                 this.fillAuth(res.data);   
39291                 this.checkFails =0;
39292                 Roo.XComponent.build();
39293             },
39294             failure : this.show
39295         });
39296         
39297     }, 
39298     
39299     
39300     check: function(cfg) // called every so often to refresh cookie etc..
39301     {
39302         if (cfg.again) { // could be undefined..
39303             this.checkFails++;
39304         } else {
39305             this.checkFails = 0;
39306         }
39307         var _this = this;
39308         if (this.sending) {
39309             if ( this.checkFails > 4) {
39310                 Roo.MessageBox.alert("Error",  
39311                     "Error getting authentication status. - try reloading, or wait a while", function() {
39312                         _this.sending = false;
39313                     }); 
39314                 return;
39315             }
39316             cfg.again = true;
39317             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
39318             return;
39319         }
39320         this.sending = true;
39321         
39322         Roo.Ajax.request({  
39323             url: this.url,
39324             params: {
39325                 getAuthUser: true
39326             },  
39327             method: this.method,
39328             success:  cfg.success || this.success,
39329             failure : cfg.failure || this.failure,
39330             scope : this,
39331             callCfg : cfg
39332               
39333         });  
39334     }, 
39335     
39336     
39337     logout: function()
39338     {
39339         window.onbeforeunload = function() { }; // false does not work for IE..
39340         this.user = false;
39341         var _this = this;
39342         
39343         Roo.Ajax.request({  
39344             url: this.url,
39345             params: {
39346                 logout: 1
39347             },  
39348             method: 'GET',
39349             failure : function() {
39350                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
39351                     document.location = document.location.toString() + '?ts=' + Math.random();
39352                 });
39353                 
39354             },
39355             success : function() {
39356                 _this.user = false;
39357                 this.checkFails =0;
39358                 // fixme..
39359                 document.location = document.location.toString() + '?ts=' + Math.random();
39360             }
39361               
39362               
39363         }); 
39364     },
39365     
39366     processResponse : function (response)
39367     {
39368         var res = '';
39369         try {
39370             res = Roo.decode(response.responseText);
39371             // oops...
39372             if (typeof(res) != 'object') {
39373                 res = { success : false, errorMsg : res, errors : true };
39374             }
39375             if (typeof(res.success) == 'undefined') {
39376                 res.success = false;
39377             }
39378             
39379         } catch(e) {
39380             res = { success : false,  errorMsg : response.responseText, errors : true };
39381         }
39382         return res;
39383     },
39384     
39385     success : function(response, opts)  // check successfull...
39386     {  
39387         this.sending = false;
39388         var res = this.processResponse(response);
39389         if (!res.success) {
39390             return this.failure(response, opts);
39391         }
39392         if (!res.data || !res.data.id) {
39393             return this.failure(response,opts);
39394         }
39395         //console.log(res);
39396         this.fillAuth(res.data);
39397         
39398         this.checkFails =0;
39399         
39400     },
39401     
39402     
39403     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
39404     {
39405         this.authUser = -1;
39406         this.sending = false;
39407         var res = this.processResponse(response);
39408         //console.log(res);
39409         if ( this.checkFails > 2) {
39410         
39411             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
39412                 "Error getting authentication status. - try reloading"); 
39413             return;
39414         }
39415         opts.callCfg.again = true;
39416         this.check.defer(1000, this, [ opts.callCfg ]);
39417         return;  
39418     },
39419     
39420     
39421     
39422     fillAuth: function(au) {
39423         this.startAuthCheck();
39424         this.authUserId = au.id;
39425         this.authUser = au;
39426         this.lastChecked = new Date();
39427         this.fireEvent('refreshed', au);
39428         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
39429         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
39430         au.lang = au.lang || 'en';
39431         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
39432         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
39433         this.switchLang(au.lang );
39434         
39435      
39436         // open system... - -on setyp..
39437         if (this.authUserId  < 0) {
39438             Roo.MessageBox.alert("Warning", 
39439                 "This is an open system - please set up a admin user with a password.");  
39440         }
39441          
39442         //Pman.onload(); // which should do nothing if it's a re-auth result...
39443         
39444              
39445     },
39446     
39447     startAuthCheck : function() // starter for timeout checking..
39448     {
39449         if (this.intervalID) { // timer already in place...
39450             return false;
39451         }
39452         var _this = this;
39453         this.intervalID =  window.setInterval(function() {
39454               _this.check(false);
39455             }, 120000); // every 120 secs = 2mins..
39456         
39457         
39458     },
39459          
39460     
39461     switchLang : function (lang) 
39462     {
39463         _T = typeof(_T) == 'undefined' ? false : _T;
39464           if (!_T || !lang.length) {
39465             return;
39466         }
39467         
39468         if (!_T && lang != 'en') {
39469             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
39470             return;
39471         }
39472         
39473         if (typeof(_T.en) == 'undefined') {
39474             _T.en = {};
39475             Roo.apply(_T.en, _T);
39476         }
39477         
39478         if (typeof(_T[lang]) == 'undefined') {
39479             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
39480             return;
39481         }
39482         
39483         
39484         Roo.apply(_T, _T[lang]);
39485         // just need to set the text values for everything...
39486         var _this = this;
39487         /* this will not work ...
39488         if (this.form) { 
39489             
39490                
39491             function formLabel(name, val) {
39492                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
39493             }
39494             
39495             formLabel('password', "Password"+':');
39496             formLabel('username', "Email Address"+':');
39497             formLabel('lang', "Language"+':');
39498             this.dialog.setTitle("Login");
39499             this.dialog.buttons[0].setText("Forgot Password");
39500             this.dialog.buttons[1].setText("Login");
39501         }
39502         */
39503         
39504         
39505     },
39506     
39507     
39508     title: "Login",
39509     modal: true,
39510     width:  350,
39511     //height: 230,
39512     height: 180,
39513     shadow: true,
39514     minWidth:200,
39515     minHeight:180,
39516     //proxyDrag: true,
39517     closable: false,
39518     draggable: false,
39519     collapsible: false,
39520     resizable: false,
39521     center: {  // needed??
39522         autoScroll:false,
39523         titlebar: false,
39524        // tabPosition: 'top',
39525         hideTabs: true,
39526         closeOnTab: true,
39527         alwaysShowTabs: false
39528     } ,
39529     listeners : {
39530         
39531         show  : function(dlg)
39532         {
39533             //console.log(this);
39534             this.form = this.layout.getRegion('center').activePanel.form;
39535             this.form.dialog = dlg;
39536             this.buttons[0].form = this.form;
39537             this.buttons[0].dialog = dlg;
39538             this.buttons[1].form = this.form;
39539             this.buttons[1].dialog = dlg;
39540            
39541            //this.resizeToLogo.defer(1000,this);
39542             // this is all related to resizing for logos..
39543             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
39544            //// if (!sz) {
39545              //   this.resizeToLogo.defer(1000,this);
39546              //   return;
39547            // }
39548             //var w = Ext.lib.Dom.getViewWidth() - 100;
39549             //var h = Ext.lib.Dom.getViewHeight() - 100;
39550             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
39551             //this.center();
39552             if (this.disabled) {
39553                 this.hide();
39554                 return;
39555             }
39556             
39557             if (this.user.id < 0) { // used for inital setup situations.
39558                 return;
39559             }
39560             
39561             if (this.intervalID) {
39562                 // remove the timer
39563                 window.clearInterval(this.intervalID);
39564                 this.intervalID = false;
39565             }
39566             
39567             
39568             if (Roo.get('loading')) {
39569                 Roo.get('loading').remove();
39570             }
39571             if (Roo.get('loading-mask')) {
39572                 Roo.get('loading-mask').hide();
39573             }
39574             
39575             //incomming._node = tnode;
39576             this.form.reset();
39577             //this.dialog.modal = !modal;
39578             //this.dialog.show();
39579             this.el.unmask(); 
39580             
39581             
39582             this.form.setValues({
39583                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
39584                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
39585             });
39586             
39587             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
39588             if (this.form.findField('username').getValue().length > 0 ){
39589                 this.form.findField('password').focus();
39590             } else {
39591                this.form.findField('username').focus();
39592             }
39593     
39594         }
39595     },
39596     items : [
39597          {
39598        
39599             xtype : 'ContentPanel',
39600             xns : Roo,
39601             region: 'center',
39602             fitToFrame : true,
39603             
39604             items : [
39605     
39606                 {
39607                
39608                     xtype : 'Form',
39609                     xns : Roo.form,
39610                     labelWidth: 100,
39611                     style : 'margin: 10px;',
39612                     
39613                     listeners : {
39614                         actionfailed : function(f, act) {
39615                             // form can return { errors: .... }
39616                                 
39617                             //act.result.errors // invalid form element list...
39618                             //act.result.errorMsg// invalid form element list...
39619                             
39620                             this.dialog.el.unmask();
39621                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
39622                                         "Login failed - communication error - try again.");
39623                                       
39624                         },
39625                         actioncomplete: function(re, act) {
39626                              
39627                             Roo.state.Manager.set(
39628                                 this.dialog.realm + '.username',  
39629                                     this.findField('username').getValue()
39630                             );
39631                             Roo.state.Manager.set(
39632                                 this.dialog.realm + '.lang',  
39633                                 this.findField('lang').getValue() 
39634                             );
39635                             
39636                             this.dialog.fillAuth(act.result.data);
39637                               
39638                             this.dialog.hide();
39639                             
39640                             if (Roo.get('loading-mask')) {
39641                                 Roo.get('loading-mask').show();
39642                             }
39643                             Roo.XComponent.build();
39644                             
39645                              
39646                             
39647                         }
39648                     },
39649                     items : [
39650                         {
39651                             xtype : 'TextField',
39652                             xns : Roo.form,
39653                             fieldLabel: "Email Address",
39654                             name: 'username',
39655                             width:200,
39656                             autoCreate : {tag: "input", type: "text", size: "20"}
39657                         },
39658                         {
39659                             xtype : 'TextField',
39660                             xns : Roo.form,
39661                             fieldLabel: "Password",
39662                             inputType: 'password',
39663                             name: 'password',
39664                             width:200,
39665                             autoCreate : {tag: "input", type: "text", size: "20"},
39666                             listeners : {
39667                                 specialkey : function(e,ev) {
39668                                     if (ev.keyCode == 13) {
39669                                         this.form.dialog.el.mask("Logging in");
39670                                         this.form.doAction('submit', {
39671                                             url: this.form.dialog.url,
39672                                             method: this.form.dialog.method
39673                                         });
39674                                     }
39675                                 }
39676                             }  
39677                         },
39678                         {
39679                             xtype : 'ComboBox',
39680                             xns : Roo.form,
39681                             fieldLabel: "Language",
39682                             name : 'langdisp',
39683                             store: {
39684                                 xtype : 'SimpleStore',
39685                                 fields: ['lang', 'ldisp'],
39686                                 data : [
39687                                     [ 'en', 'English' ],
39688                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
39689                                     [ 'zh_CN', '\u7C21\u4E2D' ]
39690                                 ]
39691                             },
39692                             
39693                             valueField : 'lang',
39694                             hiddenName:  'lang',
39695                             width: 200,
39696                             displayField:'ldisp',
39697                             typeAhead: false,
39698                             editable: false,
39699                             mode: 'local',
39700                             triggerAction: 'all',
39701                             emptyText:'Select a Language...',
39702                             selectOnFocus:true,
39703                             listeners : {
39704                                 select :  function(cb, rec, ix) {
39705                                     this.form.switchLang(rec.data.lang);
39706                                 }
39707                             }
39708                         
39709                         }
39710                     ]
39711                 }
39712                   
39713                 
39714             ]
39715         }
39716     ],
39717     buttons : [
39718         {
39719             xtype : 'Button',
39720             xns : 'Roo',
39721             text : "Forgot Password",
39722             listeners : {
39723                 click : function() {
39724                     //console.log(this);
39725                     var n = this.form.findField('username').getValue();
39726                     if (!n.length) {
39727                         Roo.MessageBox.alert("Error", "Fill in your email address");
39728                         return;
39729                     }
39730                     Roo.Ajax.request({
39731                         url: this.dialog.url,
39732                         params: {
39733                             passwordRequest: n
39734                         },
39735                         method: this.dialog.method,
39736                         success:  function(response, opts)  {  // check successfull...
39737                         
39738                             var res = this.dialog.processResponse(response);
39739                             if (!res.success) { // error!
39740                                Roo.MessageBox.alert("Error" ,
39741                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
39742                                return;
39743                             }
39744                             Roo.MessageBox.alert("Notice" ,
39745                                 "Please check you email for the Password Reset message");
39746                         },
39747                         failure : function() {
39748                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
39749                         }
39750                         
39751                     });
39752                 }
39753             }
39754         },
39755         {
39756             xtype : 'Button',
39757             xns : 'Roo',
39758             text : "Login",
39759             listeners : {
39760                 
39761                 click : function () {
39762                         
39763                     this.dialog.el.mask("Logging in");
39764                     this.form.doAction('submit', {
39765                             url: this.dialog.url,
39766                             method: this.dialog.method
39767                     });
39768                 }
39769             }
39770         }
39771     ]
39772   
39773   
39774 })
39775  
39776
39777
39778