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             
24246         }
24247         if (typeof(v) == 'object') {
24248             // then let's assume it's an array of objects..
24249             Roo.each(v, function(l) {
24250                 this.addItem(l);
24251             }, this);
24252              
24253         }
24254         
24255         
24256     },
24257     setFromData: function(v)
24258     {
24259         // this recieves an object, if setValues is called.
24260         this.reset();
24261         this.el.dom.value = v[this.displayField];
24262         this.hiddenEl.dom.value = v[this.valueField];
24263         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24264             return;
24265         }
24266         var keys = v[this.valueField].split(',');
24267         var display = v[this.displayField].split(',');
24268         for (var i = 0 ; i < keys.length; i++) {
24269             
24270             add = {};
24271             add[this.valueField] = keys[i];
24272             add[this.displayField] = display[i];
24273             this.addItem(add);
24274         }
24275       
24276         
24277     },
24278     
24279     
24280     validateValue : function(value){
24281         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24282         
24283     }
24284     
24285 });
24286
24287
24288
24289 /**
24290  * @class Roo.form.ComboBoxArray.Item
24291  * @extends Roo.BoxComponent
24292  * A selected item in the list
24293  *  Fred [x]  Brian [x]  [Pick another |v]
24294  * 
24295  * @constructor
24296  * Create a new item.
24297  * @param {Object} config Configuration options
24298  */
24299  
24300 Roo.form.ComboBoxArray.Item = function(config) {
24301     config.id = Roo.id();
24302     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24303 }
24304
24305 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24306     data : {},
24307     cb: false,
24308     displayField : false,
24309     tipField : false,
24310     
24311     
24312     defaultAutoCreate : {
24313         tag: 'div',
24314         cls: 'x-cbarray-item',
24315         cn : [ 
24316             { tag: 'div' },
24317             {
24318                 tag: 'img',
24319                 width:16,
24320                 height : 16,
24321                 src : Roo.BLANK_IMAGE_URL ,
24322                 align: 'center'
24323             }
24324         ]
24325         
24326     },
24327     
24328  
24329     onRender : function(ct, position)
24330     {
24331         Roo.form.Field.superclass.onRender.call(this, ct, position);
24332         
24333         if(!this.el){
24334             var cfg = this.getAutoCreate();
24335             this.el = ct.createChild(cfg, position);
24336         }
24337         
24338         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24339         
24340         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24341             this.cb.renderer(this.data) :
24342             String.format('{0}',this.data[this.displayField]);
24343         
24344             
24345         this.el.child('div').dom.setAttribute('qtip',
24346                         String.format('{0}',this.data[this.tipField])
24347         );
24348         
24349         this.el.child('img').on('click', this.remove, this);
24350         
24351     },
24352    
24353     remove : function()
24354     {
24355         
24356         this.cb.items.remove(this);
24357         this.el.child('img').un('click', this.remove, this);
24358         this.el.remove();
24359         this.cb.updateHiddenEl();
24360     }
24361     
24362     
24363 });/*
24364  * Based on:
24365  * Ext JS Library 1.1.1
24366  * Copyright(c) 2006-2007, Ext JS, LLC.
24367  *
24368  * Originally Released Under LGPL - original licence link has changed is not relivant.
24369  *
24370  * Fork - LGPL
24371  * <script type="text/javascript">
24372  */
24373 /**
24374  * @class Roo.form.Checkbox
24375  * @extends Roo.form.Field
24376  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24377  * @constructor
24378  * Creates a new Checkbox
24379  * @param {Object} config Configuration options
24380  */
24381 Roo.form.Checkbox = function(config){
24382     Roo.form.Checkbox.superclass.constructor.call(this, config);
24383     this.addEvents({
24384         /**
24385          * @event check
24386          * Fires when the checkbox is checked or unchecked.
24387              * @param {Roo.form.Checkbox} this This checkbox
24388              * @param {Boolean} checked The new checked value
24389              */
24390         check : true
24391     });
24392 };
24393
24394 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24395     /**
24396      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24397      */
24398     focusClass : undefined,
24399     /**
24400      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24401      */
24402     fieldClass: "x-form-field",
24403     /**
24404      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24405      */
24406     checked: false,
24407     /**
24408      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24409      * {tag: "input", type: "checkbox", autocomplete: "off"})
24410      */
24411     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24412     /**
24413      * @cfg {String} boxLabel The text that appears beside the checkbox
24414      */
24415     boxLabel : "",
24416     /**
24417      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24418      */  
24419     inputValue : '1',
24420     /**
24421      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24422      */
24423      valueOff: '0', // value when not checked..
24424
24425     actionMode : 'viewEl', 
24426     //
24427     // private
24428     itemCls : 'x-menu-check-item x-form-item',
24429     groupClass : 'x-menu-group-item',
24430     inputType : 'hidden',
24431     
24432     
24433     inSetChecked: false, // check that we are not calling self...
24434     
24435     inputElement: false, // real input element?
24436     basedOn: false, // ????
24437     
24438     isFormField: true, // not sure where this is needed!!!!
24439
24440     onResize : function(){
24441         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24442         if(!this.boxLabel){
24443             this.el.alignTo(this.wrap, 'c-c');
24444         }
24445     },
24446
24447     initEvents : function(){
24448         Roo.form.Checkbox.superclass.initEvents.call(this);
24449         this.el.on("click", this.onClick,  this);
24450         this.el.on("change", this.onClick,  this);
24451     },
24452
24453
24454     getResizeEl : function(){
24455         return this.wrap;
24456     },
24457
24458     getPositionEl : function(){
24459         return this.wrap;
24460     },
24461
24462     // private
24463     onRender : function(ct, position){
24464         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24465         /*
24466         if(this.inputValue !== undefined){
24467             this.el.dom.value = this.inputValue;
24468         }
24469         */
24470         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24471         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24472         var viewEl = this.wrap.createChild({ 
24473             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24474         this.viewEl = viewEl;   
24475         this.wrap.on('click', this.onClick,  this); 
24476         
24477         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24478         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24479         
24480         
24481         
24482         if(this.boxLabel){
24483             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24484         //    viewEl.on('click', this.onClick,  this); 
24485         }
24486         //if(this.checked){
24487             this.setChecked(this.checked);
24488         //}else{
24489             //this.checked = this.el.dom;
24490         //}
24491
24492     },
24493
24494     // private
24495     initValue : Roo.emptyFn,
24496
24497     /**
24498      * Returns the checked state of the checkbox.
24499      * @return {Boolean} True if checked, else false
24500      */
24501     getValue : function(){
24502         if(this.el){
24503             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24504         }
24505         return this.valueOff;
24506         
24507     },
24508
24509         // private
24510     onClick : function(){ 
24511         this.setChecked(!this.checked);
24512
24513         //if(this.el.dom.checked != this.checked){
24514         //    this.setValue(this.el.dom.checked);
24515        // }
24516     },
24517
24518     /**
24519      * Sets the checked state of the checkbox.
24520      * On is always based on a string comparison between inputValue and the param.
24521      * @param {Boolean/String} value - the value to set 
24522      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24523      */
24524     setValue : function(v,suppressEvent){
24525         
24526         
24527         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24528         //if(this.el && this.el.dom){
24529         //    this.el.dom.checked = this.checked;
24530         //    this.el.dom.defaultChecked = this.checked;
24531         //}
24532         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24533         //this.fireEvent("check", this, this.checked);
24534     },
24535     // private..
24536     setChecked : function(state,suppressEvent)
24537     {
24538         if (this.inSetChecked) {
24539             this.checked = state;
24540             return;
24541         }
24542         
24543     
24544         if(this.wrap){
24545             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24546         }
24547         this.checked = state;
24548         if(suppressEvent !== true){
24549             this.fireEvent('check', this, state);
24550         }
24551         this.inSetChecked = true;
24552         this.el.dom.value = state ? this.inputValue : this.valueOff;
24553         this.inSetChecked = false;
24554         
24555     },
24556     // handle setting of hidden value by some other method!!?!?
24557     setFromHidden: function()
24558     {
24559         if(!this.el){
24560             return;
24561         }
24562         //console.log("SET FROM HIDDEN");
24563         //alert('setFrom hidden');
24564         this.setValue(this.el.dom.value);
24565     },
24566     
24567     onDestroy : function()
24568     {
24569         if(this.viewEl){
24570             Roo.get(this.viewEl).remove();
24571         }
24572          
24573         Roo.form.Checkbox.superclass.onDestroy.call(this);
24574     }
24575
24576 });/*
24577  * Based on:
24578  * Ext JS Library 1.1.1
24579  * Copyright(c) 2006-2007, Ext JS, LLC.
24580  *
24581  * Originally Released Under LGPL - original licence link has changed is not relivant.
24582  *
24583  * Fork - LGPL
24584  * <script type="text/javascript">
24585  */
24586  
24587 /**
24588  * @class Roo.form.Radio
24589  * @extends Roo.form.Checkbox
24590  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24591  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24592  * @constructor
24593  * Creates a new Radio
24594  * @param {Object} config Configuration options
24595  */
24596 Roo.form.Radio = function(){
24597     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24598 };
24599 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24600     inputType: 'radio',
24601
24602     /**
24603      * If this radio is part of a group, it will return the selected value
24604      * @return {String}
24605      */
24606     getGroupValue : function(){
24607         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24608     }
24609 });//<script type="text/javascript">
24610
24611 /*
24612  * Ext JS Library 1.1.1
24613  * Copyright(c) 2006-2007, Ext JS, LLC.
24614  * licensing@extjs.com
24615  * 
24616  * http://www.extjs.com/license
24617  */
24618  
24619  /*
24620   * 
24621   * Known bugs:
24622   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
24623   * - IE ? - no idea how much works there.
24624   * 
24625   * 
24626   * 
24627   */
24628  
24629
24630 /**
24631  * @class Ext.form.HtmlEditor
24632  * @extends Ext.form.Field
24633  * Provides a lightweight HTML Editor component.
24634  *
24635  * This has been tested on Fireforx / Chrome.. IE may not be so great..
24636  * 
24637  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
24638  * supported by this editor.</b><br/><br/>
24639  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
24640  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24641  */
24642 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
24643       /**
24644      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24645      */
24646     toolbars : false,
24647     /**
24648      * @cfg {String} createLinkText The default text for the create link prompt
24649      */
24650     createLinkText : 'Please enter the URL for the link:',
24651     /**
24652      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
24653      */
24654     defaultLinkValue : 'http:/'+'/',
24655    
24656      /**
24657      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24658      *                        Roo.resizable.
24659      */
24660     resizable : false,
24661      /**
24662      * @cfg {Number} height (in pixels)
24663      */   
24664     height: 300,
24665    /**
24666      * @cfg {Number} width (in pixels)
24667      */   
24668     width: 500,
24669     
24670     /**
24671      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24672      * 
24673      */
24674     stylesheets: false,
24675     
24676     // id of frame..
24677     frameId: false,
24678     
24679     // private properties
24680     validationEvent : false,
24681     deferHeight: true,
24682     initialized : false,
24683     activated : false,
24684     sourceEditMode : false,
24685     onFocus : Roo.emptyFn,
24686     iframePad:3,
24687     hideMode:'offsets',
24688     
24689     defaultAutoCreate : { // modified by initCompnoent..
24690         tag: "textarea",
24691         style:"width:500px;height:300px;",
24692         autocomplete: "off"
24693     },
24694
24695     // private
24696     initComponent : function(){
24697         this.addEvents({
24698             /**
24699              * @event initialize
24700              * Fires when the editor is fully initialized (including the iframe)
24701              * @param {HtmlEditor} this
24702              */
24703             initialize: true,
24704             /**
24705              * @event activate
24706              * Fires when the editor is first receives the focus. Any insertion must wait
24707              * until after this event.
24708              * @param {HtmlEditor} this
24709              */
24710             activate: true,
24711              /**
24712              * @event beforesync
24713              * Fires before the textarea is updated with content from the editor iframe. Return false
24714              * to cancel the sync.
24715              * @param {HtmlEditor} this
24716              * @param {String} html
24717              */
24718             beforesync: true,
24719              /**
24720              * @event beforepush
24721              * Fires before the iframe editor is updated with content from the textarea. Return false
24722              * to cancel the push.
24723              * @param {HtmlEditor} this
24724              * @param {String} html
24725              */
24726             beforepush: true,
24727              /**
24728              * @event sync
24729              * Fires when the textarea is updated with content from the editor iframe.
24730              * @param {HtmlEditor} this
24731              * @param {String} html
24732              */
24733             sync: true,
24734              /**
24735              * @event push
24736              * Fires when the iframe editor is updated with content from the textarea.
24737              * @param {HtmlEditor} this
24738              * @param {String} html
24739              */
24740             push: true,
24741              /**
24742              * @event editmodechange
24743              * Fires when the editor switches edit modes
24744              * @param {HtmlEditor} this
24745              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24746              */
24747             editmodechange: true,
24748             /**
24749              * @event editorevent
24750              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24751              * @param {HtmlEditor} this
24752              */
24753             editorevent: true
24754         });
24755         this.defaultAutoCreate =  {
24756             tag: "textarea",
24757             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
24758             autocomplete: "off"
24759         };
24760     },
24761
24762     /**
24763      * Protected method that will not generally be called directly. It
24764      * is called when the editor creates its toolbar. Override this method if you need to
24765      * add custom toolbar buttons.
24766      * @param {HtmlEditor} editor
24767      */
24768     createToolbar : function(editor){
24769         if (!editor.toolbars || !editor.toolbars.length) {
24770             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
24771         }
24772         
24773         for (var i =0 ; i < editor.toolbars.length;i++) {
24774             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
24775             editor.toolbars[i].init(editor);
24776         }
24777          
24778         
24779     },
24780
24781     /**
24782      * Protected method that will not generally be called directly. It
24783      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24784      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24785      */
24786     getDocMarkup : function(){
24787         // body styles..
24788         var st = '';
24789         if (this.stylesheets === false) {
24790             
24791             Roo.get(document.head).select('style').each(function(node) {
24792                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24793             });
24794             
24795             Roo.get(document.head).select('link').each(function(node) { 
24796                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24797             });
24798             
24799         } else if (!this.stylesheets.length) {
24800                 // simple..
24801                 st = '<style type="text/css">' +
24802                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24803                    '</style>';
24804         } else {
24805             Roo.each(this.stylesheets, function(s) {
24806                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
24807             });
24808             
24809         }
24810         
24811         st +=  '<style type="text/css">' +
24812             'IMG { cursor: pointer } ' +
24813         '</style>';
24814
24815         
24816         return '<html><head>' + st  +
24817             //<style type="text/css">' +
24818             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24819             //'</style>' +
24820             ' </head><body class="roo-htmleditor-body"></body></html>';
24821     },
24822
24823     // private
24824     onRender : function(ct, position)
24825     {
24826         var _t = this;
24827         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24828         this.el.dom.style.border = '0 none';
24829         this.el.dom.setAttribute('tabIndex', -1);
24830         this.el.addClass('x-hidden');
24831         if(Roo.isIE){ // fix IE 1px bogus margin
24832             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24833         }
24834         this.wrap = this.el.wrap({
24835             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24836         });
24837         
24838         if (this.resizable) {
24839             this.resizeEl = new Roo.Resizable(this.wrap, {
24840                 pinned : true,
24841                 wrap: true,
24842                 dynamic : true,
24843                 minHeight : this.height,
24844                 height: this.height,
24845                 handles : this.resizable,
24846                 width: this.width,
24847                 listeners : {
24848                     resize : function(r, w, h) {
24849                         _t.onResize(w,h); // -something
24850                     }
24851                 }
24852             });
24853             
24854         }
24855
24856         this.frameId = Roo.id();
24857         
24858         this.createToolbar(this);
24859         
24860       
24861         
24862         var iframe = this.wrap.createChild({
24863             tag: 'iframe',
24864             id: this.frameId,
24865             name: this.frameId,
24866             frameBorder : 'no',
24867             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24868         }, this.el
24869         );
24870         
24871        // console.log(iframe);
24872         //this.wrap.dom.appendChild(iframe);
24873
24874         this.iframe = iframe.dom;
24875
24876          this.assignDocWin();
24877         
24878         this.doc.designMode = 'on';
24879        
24880         this.doc.open();
24881         this.doc.write(this.getDocMarkup());
24882         this.doc.close();
24883
24884         
24885         var task = { // must defer to wait for browser to be ready
24886             run : function(){
24887                 //console.log("run task?" + this.doc.readyState);
24888                 this.assignDocWin();
24889                 if(this.doc.body || this.doc.readyState == 'complete'){
24890                     try {
24891                         this.doc.designMode="on";
24892                     } catch (e) {
24893                         return;
24894                     }
24895                     Roo.TaskMgr.stop(task);
24896                     this.initEditor.defer(10, this);
24897                 }
24898             },
24899             interval : 10,
24900             duration:10000,
24901             scope: this
24902         };
24903         Roo.TaskMgr.start(task);
24904
24905         if(!this.width){
24906             this.setSize(this.wrap.getSize());
24907         }
24908         if (this.resizeEl) {
24909             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24910             // should trigger onReize..
24911         }
24912     },
24913
24914     // private
24915     onResize : function(w, h)
24916     {
24917         //Roo.log('resize: ' +w + ',' + h );
24918         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24919         if(this.el && this.iframe){
24920             if(typeof w == 'number'){
24921                 var aw = w - this.wrap.getFrameWidth('lr');
24922                 this.el.setWidth(this.adjustWidth('textarea', aw));
24923                 this.iframe.style.width = aw + 'px';
24924             }
24925             if(typeof h == 'number'){
24926                 var tbh = 0;
24927                 for (var i =0; i < this.toolbars.length;i++) {
24928                     // fixme - ask toolbars for heights?
24929                     tbh += this.toolbars[i].tb.el.getHeight();
24930                     if (this.toolbars[i].footer) {
24931                         tbh += this.toolbars[i].footer.el.getHeight();
24932                     }
24933                 }
24934                 
24935                 
24936                 
24937                 
24938                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24939                 ah -= 5; // knock a few pixes off for look..
24940                 this.el.setHeight(this.adjustWidth('textarea', ah));
24941                 this.iframe.style.height = ah + 'px';
24942                 if(this.doc){
24943                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24944                 }
24945             }
24946         }
24947     },
24948
24949     /**
24950      * Toggles the editor between standard and source edit mode.
24951      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24952      */
24953     toggleSourceEdit : function(sourceEditMode){
24954         
24955         this.sourceEditMode = sourceEditMode === true;
24956         
24957         if(this.sourceEditMode){
24958           
24959             this.syncValue();
24960             this.iframe.className = 'x-hidden';
24961             this.el.removeClass('x-hidden');
24962             this.el.dom.removeAttribute('tabIndex');
24963             this.el.focus();
24964         }else{
24965              
24966             this.pushValue();
24967             this.iframe.className = '';
24968             this.el.addClass('x-hidden');
24969             this.el.dom.setAttribute('tabIndex', -1);
24970             this.deferFocus();
24971         }
24972         this.setSize(this.wrap.getSize());
24973         this.fireEvent('editmodechange', this, this.sourceEditMode);
24974     },
24975
24976     // private used internally
24977     createLink : function(){
24978         var url = prompt(this.createLinkText, this.defaultLinkValue);
24979         if(url && url != 'http:/'+'/'){
24980             this.relayCmd('createlink', url);
24981         }
24982     },
24983
24984     // private (for BoxComponent)
24985     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24986
24987     // private (for BoxComponent)
24988     getResizeEl : function(){
24989         return this.wrap;
24990     },
24991
24992     // private (for BoxComponent)
24993     getPositionEl : function(){
24994         return this.wrap;
24995     },
24996
24997     // private
24998     initEvents : function(){
24999         this.originalValue = this.getValue();
25000     },
25001
25002     /**
25003      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25004      * @method
25005      */
25006     markInvalid : Roo.emptyFn,
25007     /**
25008      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25009      * @method
25010      */
25011     clearInvalid : Roo.emptyFn,
25012
25013     setValue : function(v){
25014         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25015         this.pushValue();
25016     },
25017
25018     /**
25019      * Protected method that will not generally be called directly. If you need/want
25020      * custom HTML cleanup, this is the method you should override.
25021      * @param {String} html The HTML to be cleaned
25022      * return {String} The cleaned HTML
25023      */
25024     cleanHtml : function(html){
25025         html = String(html);
25026         if(html.length > 5){
25027             if(Roo.isSafari){ // strip safari nonsense
25028                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25029             }
25030         }
25031         if(html == '&nbsp;'){
25032             html = '';
25033         }
25034         return html;
25035     },
25036
25037     /**
25038      * Protected method that will not generally be called directly. Syncs the contents
25039      * of the editor iframe with the textarea.
25040      */
25041     syncValue : function(){
25042         if(this.initialized){
25043             var bd = (this.doc.body || this.doc.documentElement);
25044             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25045             var html = bd.innerHTML;
25046             if(Roo.isSafari){
25047                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25048                 var m = bs.match(/text-align:(.*?);/i);
25049                 if(m && m[1]){
25050                     html = '<div style="'+m[0]+'">' + html + '</div>';
25051                 }
25052             }
25053             html = this.cleanHtml(html);
25054             // fix up the special chars..
25055             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25056                 return "&#"+b.charCodeAt()+";" 
25057             });
25058             if(this.fireEvent('beforesync', this, html) !== false){
25059                 this.el.dom.value = html;
25060                 this.fireEvent('sync', this, html);
25061             }
25062         }
25063     },
25064
25065     /**
25066      * Protected method that will not generally be called directly. Pushes the value of the textarea
25067      * into the iframe editor.
25068      */
25069     pushValue : function(){
25070         if(this.initialized){
25071             var v = this.el.dom.value;
25072             if(v.length < 1){
25073                 v = '&#160;';
25074             }
25075             
25076             if(this.fireEvent('beforepush', this, v) !== false){
25077                 var d = (this.doc.body || this.doc.documentElement);
25078                 d.innerHTML = v;
25079                 this.cleanUpPaste();
25080                 this.el.dom.value = d.innerHTML;
25081                 this.fireEvent('push', this, v);
25082             }
25083         }
25084     },
25085
25086     // private
25087     deferFocus : function(){
25088         this.focus.defer(10, this);
25089     },
25090
25091     // doc'ed in Field
25092     focus : function(){
25093         if(this.win && !this.sourceEditMode){
25094             this.win.focus();
25095         }else{
25096             this.el.focus();
25097         }
25098     },
25099     
25100     assignDocWin: function()
25101     {
25102         var iframe = this.iframe;
25103         
25104          if(Roo.isIE){
25105             this.doc = iframe.contentWindow.document;
25106             this.win = iframe.contentWindow;
25107         } else {
25108             if (!Roo.get(this.frameId)) {
25109                 return;
25110             }
25111             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25112             this.win = Roo.get(this.frameId).dom.contentWindow;
25113         }
25114     },
25115     
25116     // private
25117     initEditor : function(){
25118         //console.log("INIT EDITOR");
25119         this.assignDocWin();
25120         
25121         
25122         
25123         this.doc.designMode="on";
25124         this.doc.open();
25125         this.doc.write(this.getDocMarkup());
25126         this.doc.close();
25127         
25128         var dbody = (this.doc.body || this.doc.documentElement);
25129         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25130         // this copies styles from the containing element into thsi one..
25131         // not sure why we need all of this..
25132         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25133         ss['background-attachment'] = 'fixed'; // w3c
25134         dbody.bgProperties = 'fixed'; // ie
25135         Roo.DomHelper.applyStyles(dbody, ss);
25136         Roo.EventManager.on(this.doc, {
25137             //'mousedown': this.onEditorEvent,
25138             'mouseup': this.onEditorEvent,
25139             'dblclick': this.onEditorEvent,
25140             'click': this.onEditorEvent,
25141             'keyup': this.onEditorEvent,
25142             buffer:100,
25143             scope: this
25144         });
25145         if(Roo.isGecko){
25146             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25147         }
25148         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25149             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25150         }
25151         this.initialized = true;
25152
25153         this.fireEvent('initialize', this);
25154         this.pushValue();
25155     },
25156
25157     // private
25158     onDestroy : function(){
25159         
25160         
25161         
25162         if(this.rendered){
25163             
25164             for (var i =0; i < this.toolbars.length;i++) {
25165                 // fixme - ask toolbars for heights?
25166                 this.toolbars[i].onDestroy();
25167             }
25168             
25169             this.wrap.dom.innerHTML = '';
25170             this.wrap.remove();
25171         }
25172     },
25173
25174     // private
25175     onFirstFocus : function(){
25176         
25177         this.assignDocWin();
25178         
25179         
25180         this.activated = true;
25181         for (var i =0; i < this.toolbars.length;i++) {
25182             this.toolbars[i].onFirstFocus();
25183         }
25184        
25185         if(Roo.isGecko){ // prevent silly gecko errors
25186             this.win.focus();
25187             var s = this.win.getSelection();
25188             if(!s.focusNode || s.focusNode.nodeType != 3){
25189                 var r = s.getRangeAt(0);
25190                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25191                 r.collapse(true);
25192                 this.deferFocus();
25193             }
25194             try{
25195                 this.execCmd('useCSS', true);
25196                 this.execCmd('styleWithCSS', false);
25197             }catch(e){}
25198         }
25199         this.fireEvent('activate', this);
25200     },
25201
25202     // private
25203     adjustFont: function(btn){
25204         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25205         //if(Roo.isSafari){ // safari
25206         //    adjust *= 2;
25207        // }
25208         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25209         if(Roo.isSafari){ // safari
25210             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25211             v =  (v < 10) ? 10 : v;
25212             v =  (v > 48) ? 48 : v;
25213             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25214             
25215         }
25216         
25217         
25218         v = Math.max(1, v+adjust);
25219         
25220         this.execCmd('FontSize', v  );
25221     },
25222
25223     onEditorEvent : function(e){
25224         this.fireEvent('editorevent', this, e);
25225       //  this.updateToolbar();
25226         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25227     },
25228
25229     insertTag : function(tg)
25230     {
25231         // could be a bit smarter... -> wrap the current selected tRoo..
25232         
25233         this.execCmd("formatblock",   tg);
25234         
25235     },
25236     
25237     insertText : function(txt)
25238     {
25239         
25240         
25241         range = this.createRange();
25242         range.deleteContents();
25243                //alert(Sender.getAttribute('label'));
25244                
25245         range.insertNode(this.doc.createTextNode(txt));
25246     } ,
25247     
25248     // private
25249     relayBtnCmd : function(btn){
25250         this.relayCmd(btn.cmd);
25251     },
25252
25253     /**
25254      * Executes a Midas editor command on the editor document and performs necessary focus and
25255      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25256      * @param {String} cmd The Midas command
25257      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25258      */
25259     relayCmd : function(cmd, value){
25260         this.win.focus();
25261         this.execCmd(cmd, value);
25262         this.fireEvent('editorevent', this);
25263         //this.updateToolbar();
25264         this.deferFocus();
25265     },
25266
25267     /**
25268      * Executes a Midas editor command directly on the editor document.
25269      * For visual commands, you should use {@link #relayCmd} instead.
25270      * <b>This should only be called after the editor is initialized.</b>
25271      * @param {String} cmd The Midas command
25272      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25273      */
25274     execCmd : function(cmd, value){
25275         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25276         this.syncValue();
25277     },
25278  
25279  
25280    
25281     /**
25282      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25283      * to insert tRoo.
25284      * @param {String} text | dom node.. 
25285      */
25286     insertAtCursor : function(text)
25287     {
25288         
25289         
25290         
25291         if(!this.activated){
25292             return;
25293         }
25294         /*
25295         if(Roo.isIE){
25296             this.win.focus();
25297             var r = this.doc.selection.createRange();
25298             if(r){
25299                 r.collapse(true);
25300                 r.pasteHTML(text);
25301                 this.syncValue();
25302                 this.deferFocus();
25303             
25304             }
25305             return;
25306         }
25307         */
25308         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25309             this.win.focus();
25310             
25311             
25312             // from jquery ui (MIT licenced)
25313             var range, node;
25314             var win = this.win;
25315             
25316             if (win.getSelection && win.getSelection().getRangeAt) {
25317                 range = win.getSelection().getRangeAt(0);
25318                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25319                 range.insertNode(node);
25320             } else if (win.document.selection && win.document.selection.createRange) {
25321                 // no firefox support
25322                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25323                 win.document.selection.createRange().pasteHTML(txt);
25324             } else {
25325                 // no firefox support
25326                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25327                 this.execCmd('InsertHTML', txt);
25328             } 
25329             
25330             this.syncValue();
25331             
25332             this.deferFocus();
25333         }
25334     },
25335  // private
25336     mozKeyPress : function(e){
25337         if(e.ctrlKey){
25338             var c = e.getCharCode(), cmd;
25339           
25340             if(c > 0){
25341                 c = String.fromCharCode(c).toLowerCase();
25342                 switch(c){
25343                     case 'b':
25344                         cmd = 'bold';
25345                         break;
25346                     case 'i':
25347                         cmd = 'italic';
25348                         break;
25349                     
25350                     case 'u':
25351                         cmd = 'underline';
25352                         break;
25353                     
25354                     case 'v':
25355                         this.cleanUpPaste.defer(100, this);
25356                         return;
25357                         
25358                 }
25359                 if(cmd){
25360                     this.win.focus();
25361                     this.execCmd(cmd);
25362                     this.deferFocus();
25363                     e.preventDefault();
25364                 }
25365                 
25366             }
25367         }
25368     },
25369
25370     // private
25371     fixKeys : function(){ // load time branching for fastest keydown performance
25372         if(Roo.isIE){
25373             return function(e){
25374                 var k = e.getKey(), r;
25375                 if(k == e.TAB){
25376                     e.stopEvent();
25377                     r = this.doc.selection.createRange();
25378                     if(r){
25379                         r.collapse(true);
25380                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25381                         this.deferFocus();
25382                     }
25383                     return;
25384                 }
25385                 
25386                 if(k == e.ENTER){
25387                     r = this.doc.selection.createRange();
25388                     if(r){
25389                         var target = r.parentElement();
25390                         if(!target || target.tagName.toLowerCase() != 'li'){
25391                             e.stopEvent();
25392                             r.pasteHTML('<br />');
25393                             r.collapse(false);
25394                             r.select();
25395                         }
25396                     }
25397                 }
25398                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25399                     this.cleanUpPaste.defer(100, this);
25400                     return;
25401                 }
25402                 
25403                 
25404             };
25405         }else if(Roo.isOpera){
25406             return function(e){
25407                 var k = e.getKey();
25408                 if(k == e.TAB){
25409                     e.stopEvent();
25410                     this.win.focus();
25411                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25412                     this.deferFocus();
25413                 }
25414                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25415                     this.cleanUpPaste.defer(100, this);
25416                     return;
25417                 }
25418                 
25419             };
25420         }else if(Roo.isSafari){
25421             return function(e){
25422                 var k = e.getKey();
25423                 
25424                 if(k == e.TAB){
25425                     e.stopEvent();
25426                     this.execCmd('InsertText','\t');
25427                     this.deferFocus();
25428                     return;
25429                 }
25430                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25431                     this.cleanUpPaste.defer(100, this);
25432                     return;
25433                 }
25434                 
25435              };
25436         }
25437     }(),
25438     
25439     getAllAncestors: function()
25440     {
25441         var p = this.getSelectedNode();
25442         var a = [];
25443         if (!p) {
25444             a.push(p); // push blank onto stack..
25445             p = this.getParentElement();
25446         }
25447         
25448         
25449         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25450             a.push(p);
25451             p = p.parentNode;
25452         }
25453         a.push(this.doc.body);
25454         return a;
25455     },
25456     lastSel : false,
25457     lastSelNode : false,
25458     
25459     
25460     getSelection : function() 
25461     {
25462         this.assignDocWin();
25463         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25464     },
25465     
25466     getSelectedNode: function() 
25467     {
25468         // this may only work on Gecko!!!
25469         
25470         // should we cache this!!!!
25471         
25472         
25473         
25474          
25475         var range = this.createRange(this.getSelection()).cloneRange();
25476         
25477         if (Roo.isIE) {
25478             var parent = range.parentElement();
25479             while (true) {
25480                 var testRange = range.duplicate();
25481                 testRange.moveToElementText(parent);
25482                 if (testRange.inRange(range)) {
25483                     break;
25484                 }
25485                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25486                     break;
25487                 }
25488                 parent = parent.parentElement;
25489             }
25490             return parent;
25491         }
25492         
25493         // is ancestor a text element.
25494         var ac =  range.commonAncestorContainer;
25495         if (ac.nodeType == 3) {
25496             ac = ac.parentNode;
25497         }
25498         
25499         var ar = ac.childNodes;
25500          
25501         var nodes = [];
25502         var other_nodes = [];
25503         var has_other_nodes = false;
25504         for (var i=0;i<ar.length;i++) {
25505             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25506                 continue;
25507             }
25508             // fullly contained node.
25509             
25510             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25511                 nodes.push(ar[i]);
25512                 continue;
25513             }
25514             
25515             // probably selected..
25516             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25517                 other_nodes.push(ar[i]);
25518                 continue;
25519             }
25520             // outer..
25521             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25522                 continue;
25523             }
25524             
25525             
25526             has_other_nodes = true;
25527         }
25528         if (!nodes.length && other_nodes.length) {
25529             nodes= other_nodes;
25530         }
25531         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25532             return false;
25533         }
25534         
25535         return nodes[0];
25536     },
25537     createRange: function(sel)
25538     {
25539         // this has strange effects when using with 
25540         // top toolbar - not sure if it's a great idea.
25541         //this.editor.contentWindow.focus();
25542         if (typeof sel != "undefined") {
25543             try {
25544                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25545             } catch(e) {
25546                 return this.doc.createRange();
25547             }
25548         } else {
25549             return this.doc.createRange();
25550         }
25551     },
25552     getParentElement: function()
25553     {
25554         
25555         this.assignDocWin();
25556         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25557         
25558         var range = this.createRange(sel);
25559          
25560         try {
25561             var p = range.commonAncestorContainer;
25562             while (p.nodeType == 3) { // text node
25563                 p = p.parentNode;
25564             }
25565             return p;
25566         } catch (e) {
25567             return null;
25568         }
25569     
25570     },
25571     /***
25572      *
25573      * Range intersection.. the hard stuff...
25574      *  '-1' = before
25575      *  '0' = hits..
25576      *  '1' = after.
25577      *         [ -- selected range --- ]
25578      *   [fail]                        [fail]
25579      *
25580      *    basically..
25581      *      if end is before start or  hits it. fail.
25582      *      if start is after end or hits it fail.
25583      *
25584      *   if either hits (but other is outside. - then it's not 
25585      *   
25586      *    
25587      **/
25588     
25589     
25590     // @see http://www.thismuchiknow.co.uk/?p=64.
25591     rangeIntersectsNode : function(range, node)
25592     {
25593         var nodeRange = node.ownerDocument.createRange();
25594         try {
25595             nodeRange.selectNode(node);
25596         } catch (e) {
25597             nodeRange.selectNodeContents(node);
25598         }
25599     
25600         var rangeStartRange = range.cloneRange();
25601         rangeStartRange.collapse(true);
25602     
25603         var rangeEndRange = range.cloneRange();
25604         rangeEndRange.collapse(false);
25605     
25606         var nodeStartRange = nodeRange.cloneRange();
25607         nodeStartRange.collapse(true);
25608     
25609         var nodeEndRange = nodeRange.cloneRange();
25610         nodeEndRange.collapse(false);
25611     
25612         return rangeStartRange.compareBoundaryPoints(
25613                  Range.START_TO_START, nodeEndRange) == -1 &&
25614                rangeEndRange.compareBoundaryPoints(
25615                  Range.START_TO_START, nodeStartRange) == 1;
25616         
25617          
25618     },
25619     rangeCompareNode : function(range, node)
25620     {
25621         var nodeRange = node.ownerDocument.createRange();
25622         try {
25623             nodeRange.selectNode(node);
25624         } catch (e) {
25625             nodeRange.selectNodeContents(node);
25626         }
25627         
25628         
25629         range.collapse(true);
25630     
25631         nodeRange.collapse(true);
25632      
25633         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25634         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25635          
25636         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25637         
25638         var nodeIsBefore   =  ss == 1;
25639         var nodeIsAfter    = ee == -1;
25640         
25641         if (nodeIsBefore && nodeIsAfter)
25642             return 0; // outer
25643         if (!nodeIsBefore && nodeIsAfter)
25644             return 1; //right trailed.
25645         
25646         if (nodeIsBefore && !nodeIsAfter)
25647             return 2;  // left trailed.
25648         // fully contined.
25649         return 3;
25650     },
25651
25652     // private? - in a new class?
25653     cleanUpPaste :  function()
25654     {
25655         // cleans up the whole document..
25656          Roo.log('cleanuppaste');
25657         this.cleanUpChildren(this.doc.body);
25658         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25659         if (clean != this.doc.body.innerHTML) {
25660             this.doc.body.innerHTML = clean;
25661         }
25662         
25663     },
25664     
25665     cleanWordChars : function(input) {
25666         var he = Roo.form.HtmlEditor;
25667     
25668         var output = input;
25669         Roo.each(he.swapCodes, function(sw) { 
25670         
25671             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25672             output = output.replace(swapper, sw[1]);
25673         });
25674         return output;
25675     },
25676     
25677     
25678     cleanUpChildren : function (n)
25679     {
25680         if (!n.childNodes.length) {
25681             return;
25682         }
25683         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25684            this.cleanUpChild(n.childNodes[i]);
25685         }
25686     },
25687     
25688     
25689         
25690     
25691     cleanUpChild : function (node)
25692     {
25693         //console.log(node);
25694         if (node.nodeName == "#text") {
25695             // clean up silly Windows -- stuff?
25696             return; 
25697         }
25698         if (node.nodeName == "#comment") {
25699             node.parentNode.removeChild(node);
25700             // clean up silly Windows -- stuff?
25701             return; 
25702         }
25703         
25704         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
25705             // remove node.
25706             node.parentNode.removeChild(node);
25707             return;
25708             
25709         }
25710         
25711         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
25712         
25713         // remove <a name=....> as rendering on yahoo mailer is bored with this.
25714         
25715         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25716             remove_keep_children = true;
25717         }
25718         
25719         if (remove_keep_children) {
25720             this.cleanUpChildren(node);
25721             // inserts everything just before this node...
25722             while (node.childNodes.length) {
25723                 var cn = node.childNodes[0];
25724                 node.removeChild(cn);
25725                 node.parentNode.insertBefore(cn, node);
25726             }
25727             node.parentNode.removeChild(node);
25728             return;
25729         }
25730         
25731         if (!node.attributes || !node.attributes.length) {
25732             this.cleanUpChildren(node);
25733             return;
25734         }
25735         
25736         function cleanAttr(n,v)
25737         {
25738             
25739             if (v.match(/^\./) || v.match(/^\//)) {
25740                 return;
25741             }
25742             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25743                 return;
25744             }
25745             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
25746             node.removeAttribute(n);
25747             
25748         }
25749         
25750         function cleanStyle(n,v)
25751         {
25752             if (v.match(/expression/)) { //XSS?? should we even bother..
25753                 node.removeAttribute(n);
25754                 return;
25755             }
25756             
25757             
25758             var parts = v.split(/;/);
25759             Roo.each(parts, function(p) {
25760                 p = p.replace(/\s+/g,'');
25761                 if (!p.length) {
25762                     return true;
25763                 }
25764                 var l = p.split(':').shift().replace(/\s+/g,'');
25765                 
25766                 // only allow 'c whitelisted system attributes'
25767                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
25768                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
25769                     node.removeAttribute(n);
25770                     return false;
25771                 }
25772                 return true;
25773             });
25774             
25775             
25776         }
25777         
25778         
25779         for (var i = node.attributes.length-1; i > -1 ; i--) {
25780             var a = node.attributes[i];
25781             //console.log(a);
25782             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
25783                 node.removeAttribute(a.name);
25784                 return;
25785             }
25786             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
25787                 cleanAttr(a.name,a.value); // fixme..
25788                 return;
25789             }
25790             if (a.name == 'style') {
25791                 cleanStyle(a.name,a.value);
25792             }
25793             /// clean up MS crap..
25794             // tecnically this should be a list of valid class'es..
25795             
25796             
25797             if (a.name == 'class') {
25798                 if (a.value.match(/^Mso/)) {
25799                     node.className = '';
25800                 }
25801                 
25802                 if (a.value.match(/body/)) {
25803                     node.className = '';
25804                 }
25805             }
25806             
25807             // style cleanup!?
25808             // class cleanup?
25809             
25810         }
25811         
25812         
25813         this.cleanUpChildren(node);
25814         
25815         
25816     }
25817     
25818     
25819     // hide stuff that is not compatible
25820     /**
25821      * @event blur
25822      * @hide
25823      */
25824     /**
25825      * @event change
25826      * @hide
25827      */
25828     /**
25829      * @event focus
25830      * @hide
25831      */
25832     /**
25833      * @event specialkey
25834      * @hide
25835      */
25836     /**
25837      * @cfg {String} fieldClass @hide
25838      */
25839     /**
25840      * @cfg {String} focusClass @hide
25841      */
25842     /**
25843      * @cfg {String} autoCreate @hide
25844      */
25845     /**
25846      * @cfg {String} inputType @hide
25847      */
25848     /**
25849      * @cfg {String} invalidClass @hide
25850      */
25851     /**
25852      * @cfg {String} invalidText @hide
25853      */
25854     /**
25855      * @cfg {String} msgFx @hide
25856      */
25857     /**
25858      * @cfg {String} validateOnBlur @hide
25859      */
25860 });
25861
25862 Roo.form.HtmlEditor.white = [
25863         'area', 'br', 'img', 'input', 'hr', 'wbr',
25864         
25865        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25866        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25867        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25868        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25869        'table',   'ul',         'xmp', 
25870        
25871        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25872       'thead',   'tr', 
25873      
25874       'dir', 'menu', 'ol', 'ul', 'dl',
25875        
25876       'embed',  'object'
25877 ];
25878
25879
25880 Roo.form.HtmlEditor.black = [
25881     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25882         'applet', // 
25883         'base',   'basefont', 'bgsound', 'blink',  'body', 
25884         'frame',  'frameset', 'head',    'html',   'ilayer', 
25885         'iframe', 'layer',  'link',     'meta',    'object',   
25886         'script', 'style' ,'title',  'xml' // clean later..
25887 ];
25888 Roo.form.HtmlEditor.clean = [
25889     'script', 'style', 'title', 'xml'
25890 ];
25891 Roo.form.HtmlEditor.remove = [
25892     'font'
25893 ];
25894 // attributes..
25895
25896 Roo.form.HtmlEditor.ablack = [
25897     'on'
25898 ];
25899     
25900 Roo.form.HtmlEditor.aclean = [ 
25901     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25902 ];
25903
25904 // protocols..
25905 Roo.form.HtmlEditor.pwhite= [
25906         'http',  'https',  'mailto'
25907 ];
25908
25909 // white listed style attributes.
25910 Roo.form.HtmlEditor.cwhite= [
25911         'text-align',
25912         'font-size'
25913 ];
25914
25915
25916 Roo.form.HtmlEditor.swapCodes   =[ 
25917     [    8211, "--" ], 
25918     [    8212, "--" ], 
25919     [    8216,  "'" ],  
25920     [    8217, "'" ],  
25921     [    8220, '"' ],  
25922     [    8221, '"' ],  
25923     [    8226, "*" ],  
25924     [    8230, "..." ]
25925 ]; 
25926
25927     // <script type="text/javascript">
25928 /*
25929  * Based on
25930  * Ext JS Library 1.1.1
25931  * Copyright(c) 2006-2007, Ext JS, LLC.
25932  *  
25933  
25934  */
25935
25936 /**
25937  * @class Roo.form.HtmlEditorToolbar1
25938  * Basic Toolbar
25939  * 
25940  * Usage:
25941  *
25942  new Roo.form.HtmlEditor({
25943     ....
25944     toolbars : [
25945         new Roo.form.HtmlEditorToolbar1({
25946             disable : { fonts: 1 , format: 1, ..., ... , ...],
25947             btns : [ .... ]
25948         })
25949     }
25950      
25951  * 
25952  * @cfg {Object} disable List of elements to disable..
25953  * @cfg {Array} btns List of additional buttons.
25954  * 
25955  * 
25956  * NEEDS Extra CSS? 
25957  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25958  */
25959  
25960 Roo.form.HtmlEditor.ToolbarStandard = function(config)
25961 {
25962     
25963     Roo.apply(this, config);
25964     
25965     // default disabled, based on 'good practice'..
25966     this.disable = this.disable || {};
25967     Roo.applyIf(this.disable, {
25968         fontSize : true,
25969         colors : true,
25970         specialElements : true
25971     });
25972     
25973     
25974     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25975     // dont call parent... till later.
25976 }
25977
25978 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
25979     
25980     tb: false,
25981     
25982     rendered: false,
25983     
25984     editor : false,
25985     /**
25986      * @cfg {Object} disable  List of toolbar elements to disable
25987          
25988      */
25989     disable : false,
25990       /**
25991      * @cfg {Array} fontFamilies An array of available font families
25992      */
25993     fontFamilies : [
25994         'Arial',
25995         'Courier New',
25996         'Tahoma',
25997         'Times New Roman',
25998         'Verdana'
25999     ],
26000     
26001     specialChars : [
26002            "&#169;",
26003           "&#174;",     
26004           "&#8482;",    
26005           "&#163;" ,    
26006          // "&#8212;",    
26007           "&#8230;",    
26008           "&#247;" ,    
26009         //  "&#225;" ,     ?? a acute?
26010            "&#8364;"    , //Euro
26011        //   "&#8220;"    ,
26012         //  "&#8221;"    ,
26013         //  "&#8226;"    ,
26014           "&#176;"  //   , // degrees
26015
26016          // "&#233;"     , // e ecute
26017          // "&#250;"     , // u ecute?
26018     ],
26019     
26020     specialElements : [
26021         {
26022             text: "Insert Table",
26023             xtype: 'MenuItem',
26024             xns : Roo.Menu,
26025             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26026                 
26027         },
26028         {    
26029             text: "Insert Image",
26030             xtype: 'MenuItem',
26031             xns : Roo.Menu,
26032             ihtml : '<img src="about:blank"/>'
26033             
26034         }
26035         
26036          
26037     ],
26038     
26039     
26040     inputElements : [ 
26041             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26042             "input:submit", "input:button", "select", "textarea", "label" ],
26043     formats : [
26044         ["p"] ,  
26045         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26046         ["pre"],[ "code"], 
26047         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
26048     ],
26049      /**
26050      * @cfg {String} defaultFont default font to use.
26051      */
26052     defaultFont: 'tahoma',
26053    
26054     fontSelect : false,
26055     
26056     
26057     formatCombo : false,
26058     
26059     init : function(editor)
26060     {
26061         this.editor = editor;
26062         
26063         
26064         var fid = editor.frameId;
26065         var etb = this;
26066         function btn(id, toggle, handler){
26067             var xid = fid + '-'+ id ;
26068             return {
26069                 id : xid,
26070                 cmd : id,
26071                 cls : 'x-btn-icon x-edit-'+id,
26072                 enableToggle:toggle !== false,
26073                 scope: editor, // was editor...
26074                 handler:handler||editor.relayBtnCmd,
26075                 clickEvent:'mousedown',
26076                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26077                 tabIndex:-1
26078             };
26079         }
26080         
26081         
26082         
26083         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26084         this.tb = tb;
26085          // stop form submits
26086         tb.el.on('click', function(e){
26087             e.preventDefault(); // what does this do?
26088         });
26089
26090         if(!this.disable.font && !Roo.isSafari){
26091             /* why no safari for fonts
26092             editor.fontSelect = tb.el.createChild({
26093                 tag:'select',
26094                 tabIndex: -1,
26095                 cls:'x-font-select',
26096                 html: editor.createFontOptions()
26097             });
26098             editor.fontSelect.on('change', function(){
26099                 var font = editor.fontSelect.dom.value;
26100                 editor.relayCmd('fontname', font);
26101                 editor.deferFocus();
26102             }, editor);
26103             tb.add(
26104                 editor.fontSelect.dom,
26105                 '-'
26106             );
26107             */
26108         };
26109         if(!this.disable.formats){
26110             this.formatCombo = new Roo.form.ComboBox({
26111                 store: new Roo.data.SimpleStore({
26112                     id : 'tag',
26113                     fields: ['tag'],
26114                     data : this.formats // from states.js
26115                 }),
26116                 blockFocus : true,
26117                 //autoCreate : {tag: "div",  size: "20"},
26118                 displayField:'tag',
26119                 typeAhead: false,
26120                 mode: 'local',
26121                 editable : false,
26122                 triggerAction: 'all',
26123                 emptyText:'Add tag',
26124                 selectOnFocus:true,
26125                 width:135,
26126                 listeners : {
26127                     'select': function(c, r, i) {
26128                         editor.insertTag(r.get('tag'));
26129                         editor.focus();
26130                     }
26131                 }
26132
26133             });
26134             tb.addField(this.formatCombo);
26135             
26136         }
26137         
26138         if(!this.disable.format){
26139             tb.add(
26140                 btn('bold'),
26141                 btn('italic'),
26142                 btn('underline')
26143             );
26144         };
26145         if(!this.disable.fontSize){
26146             tb.add(
26147                 '-',
26148                 
26149                 
26150                 btn('increasefontsize', false, editor.adjustFont),
26151                 btn('decreasefontsize', false, editor.adjustFont)
26152             );
26153         };
26154         
26155         
26156         if(!this.disable.colors){
26157             tb.add(
26158                 '-', {
26159                     id:editor.frameId +'-forecolor',
26160                     cls:'x-btn-icon x-edit-forecolor',
26161                     clickEvent:'mousedown',
26162                     tooltip: this.buttonTips['forecolor'] || undefined,
26163                     tabIndex:-1,
26164                     menu : new Roo.menu.ColorMenu({
26165                         allowReselect: true,
26166                         focus: Roo.emptyFn,
26167                         value:'000000',
26168                         plain:true,
26169                         selectHandler: function(cp, color){
26170                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26171                             editor.deferFocus();
26172                         },
26173                         scope: editor,
26174                         clickEvent:'mousedown'
26175                     })
26176                 }, {
26177                     id:editor.frameId +'backcolor',
26178                     cls:'x-btn-icon x-edit-backcolor',
26179                     clickEvent:'mousedown',
26180                     tooltip: this.buttonTips['backcolor'] || undefined,
26181                     tabIndex:-1,
26182                     menu : new Roo.menu.ColorMenu({
26183                         focus: Roo.emptyFn,
26184                         value:'FFFFFF',
26185                         plain:true,
26186                         allowReselect: true,
26187                         selectHandler: function(cp, color){
26188                             if(Roo.isGecko){
26189                                 editor.execCmd('useCSS', false);
26190                                 editor.execCmd('hilitecolor', color);
26191                                 editor.execCmd('useCSS', true);
26192                                 editor.deferFocus();
26193                             }else{
26194                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26195                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26196                                 editor.deferFocus();
26197                             }
26198                         },
26199                         scope:editor,
26200                         clickEvent:'mousedown'
26201                     })
26202                 }
26203             );
26204         };
26205         // now add all the items...
26206         
26207
26208         if(!this.disable.alignments){
26209             tb.add(
26210                 '-',
26211                 btn('justifyleft'),
26212                 btn('justifycenter'),
26213                 btn('justifyright')
26214             );
26215         };
26216
26217         //if(!Roo.isSafari){
26218             if(!this.disable.links){
26219                 tb.add(
26220                     '-',
26221                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
26222                 );
26223             };
26224
26225             if(!this.disable.lists){
26226                 tb.add(
26227                     '-',
26228                     btn('insertorderedlist'),
26229                     btn('insertunorderedlist')
26230                 );
26231             }
26232             if(!this.disable.sourceEdit){
26233                 tb.add(
26234                     '-',
26235                     btn('sourceedit', true, function(btn){
26236                         this.toggleSourceEdit(btn.pressed);
26237                     })
26238                 );
26239             }
26240         //}
26241         
26242         var smenu = { };
26243         // special menu.. - needs to be tidied up..
26244         if (!this.disable.special) {
26245             smenu = {
26246                 text: "&#169;",
26247                 cls: 'x-edit-none',
26248                 
26249                 menu : {
26250                     items : []
26251                 }
26252             };
26253             for (var i =0; i < this.specialChars.length; i++) {
26254                 smenu.menu.items.push({
26255                     
26256                     html: this.specialChars[i],
26257                     handler: function(a,b) {
26258                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26259                         //editor.insertAtCursor(a.html);
26260                         
26261                     },
26262                     tabIndex:-1
26263                 });
26264             }
26265             
26266             
26267             tb.add(smenu);
26268             
26269             
26270         }
26271          
26272         if (!this.disable.specialElements) {
26273             var semenu = {
26274                 text: "Other;",
26275                 cls: 'x-edit-none',
26276                 menu : {
26277                     items : []
26278                 }
26279             };
26280             for (var i =0; i < this.specialElements.length; i++) {
26281                 semenu.menu.items.push(
26282                     Roo.apply({ 
26283                         handler: function(a,b) {
26284                             editor.insertAtCursor(this.ihtml);
26285                         }
26286                     }, this.specialElements[i])
26287                 );
26288                     
26289             }
26290             
26291             tb.add(semenu);
26292             
26293             
26294         }
26295          
26296         
26297         if (this.btns) {
26298             for(var i =0; i< this.btns.length;i++) {
26299                 var b = Roo.factory(this.btns[i],Roo.form);
26300                 b.cls =  'x-edit-none';
26301                 b.scope = editor;
26302                 tb.add(b);
26303             }
26304         
26305         }
26306         
26307         
26308         
26309         // disable everything...
26310         
26311         this.tb.items.each(function(item){
26312            if(item.id != editor.frameId+ '-sourceedit'){
26313                 item.disable();
26314             }
26315         });
26316         this.rendered = true;
26317         
26318         // the all the btns;
26319         editor.on('editorevent', this.updateToolbar, this);
26320         // other toolbars need to implement this..
26321         //editor.on('editmodechange', this.updateToolbar, this);
26322     },
26323     
26324     
26325     
26326     /**
26327      * Protected method that will not generally be called directly. It triggers
26328      * a toolbar update by reading the markup state of the current selection in the editor.
26329      */
26330     updateToolbar: function(){
26331
26332         if(!this.editor.activated){
26333             this.editor.onFirstFocus();
26334             return;
26335         }
26336
26337         var btns = this.tb.items.map, 
26338             doc = this.editor.doc,
26339             frameId = this.editor.frameId;
26340
26341         if(!this.disable.font && !Roo.isSafari){
26342             /*
26343             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
26344             if(name != this.fontSelect.dom.value){
26345                 this.fontSelect.dom.value = name;
26346             }
26347             */
26348         }
26349         if(!this.disable.format){
26350             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
26351             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
26352             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
26353         }
26354         if(!this.disable.alignments){
26355             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
26356             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
26357             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
26358         }
26359         if(!Roo.isSafari && !this.disable.lists){
26360             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
26361             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
26362         }
26363         
26364         var ans = this.editor.getAllAncestors();
26365         if (this.formatCombo) {
26366             
26367             
26368             var store = this.formatCombo.store;
26369             this.formatCombo.setValue("");
26370             for (var i =0; i < ans.length;i++) {
26371                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26372                     // select it..
26373                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26374                     break;
26375                 }
26376             }
26377         }
26378         
26379         
26380         
26381         // hides menus... - so this cant be on a menu...
26382         Roo.menu.MenuMgr.hideAll();
26383
26384         //this.editorsyncValue();
26385     },
26386    
26387     
26388     createFontOptions : function(){
26389         var buf = [], fs = this.fontFamilies, ff, lc;
26390         for(var i = 0, len = fs.length; i< len; i++){
26391             ff = fs[i];
26392             lc = ff.toLowerCase();
26393             buf.push(
26394                 '<option value="',lc,'" style="font-family:',ff,';"',
26395                     (this.defaultFont == lc ? ' selected="true">' : '>'),
26396                     ff,
26397                 '</option>'
26398             );
26399         }
26400         return buf.join('');
26401     },
26402     
26403     toggleSourceEdit : function(sourceEditMode){
26404         if(sourceEditMode === undefined){
26405             sourceEditMode = !this.sourceEditMode;
26406         }
26407         this.sourceEditMode = sourceEditMode === true;
26408         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
26409         // just toggle the button?
26410         if(btn.pressed !== this.editor.sourceEditMode){
26411             btn.toggle(this.editor.sourceEditMode);
26412             return;
26413         }
26414         
26415         if(this.sourceEditMode){
26416             this.tb.items.each(function(item){
26417                 if(item.cmd != 'sourceedit'){
26418                     item.disable();
26419                 }
26420             });
26421           
26422         }else{
26423             if(this.initialized){
26424                 this.tb.items.each(function(item){
26425                     item.enable();
26426                 });
26427             }
26428             
26429         }
26430         // tell the editor that it's been pressed..
26431         this.editor.toggleSourceEdit(sourceEditMode);
26432        
26433     },
26434      /**
26435      * Object collection of toolbar tooltips for the buttons in the editor. The key
26436      * is the command id associated with that button and the value is a valid QuickTips object.
26437      * For example:
26438 <pre><code>
26439 {
26440     bold : {
26441         title: 'Bold (Ctrl+B)',
26442         text: 'Make the selected text bold.',
26443         cls: 'x-html-editor-tip'
26444     },
26445     italic : {
26446         title: 'Italic (Ctrl+I)',
26447         text: 'Make the selected text italic.',
26448         cls: 'x-html-editor-tip'
26449     },
26450     ...
26451 </code></pre>
26452     * @type Object
26453      */
26454     buttonTips : {
26455         bold : {
26456             title: 'Bold (Ctrl+B)',
26457             text: 'Make the selected text bold.',
26458             cls: 'x-html-editor-tip'
26459         },
26460         italic : {
26461             title: 'Italic (Ctrl+I)',
26462             text: 'Make the selected text italic.',
26463             cls: 'x-html-editor-tip'
26464         },
26465         underline : {
26466             title: 'Underline (Ctrl+U)',
26467             text: 'Underline the selected text.',
26468             cls: 'x-html-editor-tip'
26469         },
26470         increasefontsize : {
26471             title: 'Grow Text',
26472             text: 'Increase the font size.',
26473             cls: 'x-html-editor-tip'
26474         },
26475         decreasefontsize : {
26476             title: 'Shrink Text',
26477             text: 'Decrease the font size.',
26478             cls: 'x-html-editor-tip'
26479         },
26480         backcolor : {
26481             title: 'Text Highlight Color',
26482             text: 'Change the background color of the selected text.',
26483             cls: 'x-html-editor-tip'
26484         },
26485         forecolor : {
26486             title: 'Font Color',
26487             text: 'Change the color of the selected text.',
26488             cls: 'x-html-editor-tip'
26489         },
26490         justifyleft : {
26491             title: 'Align Text Left',
26492             text: 'Align text to the left.',
26493             cls: 'x-html-editor-tip'
26494         },
26495         justifycenter : {
26496             title: 'Center Text',
26497             text: 'Center text in the editor.',
26498             cls: 'x-html-editor-tip'
26499         },
26500         justifyright : {
26501             title: 'Align Text Right',
26502             text: 'Align text to the right.',
26503             cls: 'x-html-editor-tip'
26504         },
26505         insertunorderedlist : {
26506             title: 'Bullet List',
26507             text: 'Start a bulleted list.',
26508             cls: 'x-html-editor-tip'
26509         },
26510         insertorderedlist : {
26511             title: 'Numbered List',
26512             text: 'Start a numbered list.',
26513             cls: 'x-html-editor-tip'
26514         },
26515         createlink : {
26516             title: 'Hyperlink',
26517             text: 'Make the selected text a hyperlink.',
26518             cls: 'x-html-editor-tip'
26519         },
26520         sourceedit : {
26521             title: 'Source Edit',
26522             text: 'Switch to source editing mode.',
26523             cls: 'x-html-editor-tip'
26524         }
26525     },
26526     // private
26527     onDestroy : function(){
26528         if(this.rendered){
26529             
26530             this.tb.items.each(function(item){
26531                 if(item.menu){
26532                     item.menu.removeAll();
26533                     if(item.menu.el){
26534                         item.menu.el.destroy();
26535                     }
26536                 }
26537                 item.destroy();
26538             });
26539              
26540         }
26541     },
26542     onFirstFocus: function() {
26543         this.tb.items.each(function(item){
26544            item.enable();
26545         });
26546     }
26547 });
26548
26549
26550
26551
26552 // <script type="text/javascript">
26553 /*
26554  * Based on
26555  * Ext JS Library 1.1.1
26556  * Copyright(c) 2006-2007, Ext JS, LLC.
26557  *  
26558  
26559  */
26560
26561  
26562 /**
26563  * @class Roo.form.HtmlEditor.ToolbarContext
26564  * Context Toolbar
26565  * 
26566  * Usage:
26567  *
26568  new Roo.form.HtmlEditor({
26569     ....
26570     toolbars : [
26571         { xtype: 'ToolbarStandard', styles : {} }
26572         { xtype: 'ToolbarContext', disable : {} }
26573     ]
26574 })
26575
26576      
26577  * 
26578  * @config : {Object} disable List of elements to disable.. (not done yet.)
26579  * @config : {Object} styles  Map of styles available.
26580  * 
26581  */
26582
26583 Roo.form.HtmlEditor.ToolbarContext = function(config)
26584 {
26585     
26586     Roo.apply(this, config);
26587     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26588     // dont call parent... till later.
26589     this.styles = this.styles || {};
26590 }
26591 Roo.form.HtmlEditor.ToolbarContext.types = {
26592     'IMG' : {
26593         width : {
26594             title: "Width",
26595             width: 40
26596         },
26597         height:  {
26598             title: "Height",
26599             width: 40
26600         },
26601         align: {
26602             title: "Align",
26603             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
26604             width : 80
26605             
26606         },
26607         border: {
26608             title: "Border",
26609             width: 40
26610         },
26611         alt: {
26612             title: "Alt",
26613             width: 120
26614         },
26615         src : {
26616             title: "Src",
26617             width: 220
26618         }
26619         
26620     },
26621     'A' : {
26622         name : {
26623             title: "Name",
26624             width: 50
26625         },
26626         href:  {
26627             title: "Href",
26628             width: 220
26629         } // border?
26630         
26631     },
26632     'TABLE' : {
26633         rows : {
26634             title: "Rows",
26635             width: 20
26636         },
26637         cols : {
26638             title: "Cols",
26639             width: 20
26640         },
26641         width : {
26642             title: "Width",
26643             width: 40
26644         },
26645         height : {
26646             title: "Height",
26647             width: 40
26648         },
26649         border : {
26650             title: "Border",
26651             width: 20
26652         }
26653     },
26654     'TD' : {
26655         width : {
26656             title: "Width",
26657             width: 40
26658         },
26659         height : {
26660             title: "Height",
26661             width: 40
26662         },   
26663         align: {
26664             title: "Align",
26665             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
26666             width: 80
26667         },
26668         valign: {
26669             title: "Valign",
26670             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
26671             width: 80
26672         },
26673         colspan: {
26674             title: "Colspan",
26675             width: 20
26676             
26677         }
26678     },
26679     'INPUT' : {
26680         name : {
26681             title: "name",
26682             width: 120
26683         },
26684         value : {
26685             title: "Value",
26686             width: 120
26687         },
26688         width : {
26689             title: "Width",
26690             width: 40
26691         }
26692     },
26693     'LABEL' : {
26694         'for' : {
26695             title: "For",
26696             width: 120
26697         }
26698     },
26699     'TEXTAREA' : {
26700           name : {
26701             title: "name",
26702             width: 120
26703         },
26704         rows : {
26705             title: "Rows",
26706             width: 20
26707         },
26708         cols : {
26709             title: "Cols",
26710             width: 20
26711         }
26712     },
26713     'SELECT' : {
26714         name : {
26715             title: "name",
26716             width: 120
26717         },
26718         selectoptions : {
26719             title: "Options",
26720             width: 200
26721         }
26722     },
26723     
26724     // should we really allow this??
26725     // should this just be 
26726     'BODY' : {
26727         title : {
26728             title: "title",
26729             width: 200,
26730             disabled : true
26731         }
26732     },
26733     '*' : {
26734         // empty..
26735     }
26736 };
26737
26738
26739
26740 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
26741     
26742     tb: false,
26743     
26744     rendered: false,
26745     
26746     editor : false,
26747     /**
26748      * @cfg {Object} disable  List of toolbar elements to disable
26749          
26750      */
26751     disable : false,
26752     /**
26753      * @cfg {Object} styles List of styles 
26754      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
26755      *
26756      * These must be defined in the page, so they get rendered correctly..
26757      * .headline { }
26758      * TD.underline { }
26759      * 
26760      */
26761     styles : false,
26762     
26763     
26764     
26765     toolbars : false,
26766     
26767     init : function(editor)
26768     {
26769         this.editor = editor;
26770         
26771         
26772         var fid = editor.frameId;
26773         var etb = this;
26774         function btn(id, toggle, handler){
26775             var xid = fid + '-'+ id ;
26776             return {
26777                 id : xid,
26778                 cmd : id,
26779                 cls : 'x-btn-icon x-edit-'+id,
26780                 enableToggle:toggle !== false,
26781                 scope: editor, // was editor...
26782                 handler:handler||editor.relayBtnCmd,
26783                 clickEvent:'mousedown',
26784                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26785                 tabIndex:-1
26786             };
26787         }
26788         // create a new element.
26789         var wdiv = editor.wrap.createChild({
26790                 tag: 'div'
26791             }, editor.wrap.dom.firstChild.nextSibling, true);
26792         
26793         // can we do this more than once??
26794         
26795          // stop form submits
26796       
26797  
26798         // disable everything...
26799         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26800         this.toolbars = {};
26801            
26802         for (var i in  ty) {
26803           
26804             this.toolbars[i] = this.buildToolbar(ty[i],i);
26805         }
26806         this.tb = this.toolbars.BODY;
26807         this.tb.el.show();
26808         this.buildFooter();
26809         this.footer.show();
26810         editor.on('hide', function( ) { this.footer.hide() }, this);
26811         editor.on('show', function( ) { this.footer.show() }, this);
26812         
26813          
26814         this.rendered = true;
26815         
26816         // the all the btns;
26817         editor.on('editorevent', this.updateToolbar, this);
26818         // other toolbars need to implement this..
26819         //editor.on('editmodechange', this.updateToolbar, this);
26820     },
26821     
26822     
26823     
26824     /**
26825      * Protected method that will not generally be called directly. It triggers
26826      * a toolbar update by reading the markup state of the current selection in the editor.
26827      */
26828     updateToolbar: function(editor,ev,sel){
26829
26830         //Roo.log(ev);
26831         // capture mouse up - this is handy for selecting images..
26832         // perhaps should go somewhere else...
26833         if(!this.editor.activated){
26834              this.editor.onFirstFocus();
26835             return;
26836         }
26837         
26838         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
26839         // selectNode - might want to handle IE?
26840         if (ev &&
26841             (ev.type == 'mouseup' || ev.type == 'click' ) &&
26842             ev.target && ev.target.tagName == 'IMG') {
26843             // they have click on an image...
26844             // let's see if we can change the selection...
26845             sel = ev.target;
26846          
26847               var nodeRange = sel.ownerDocument.createRange();
26848             try {
26849                 nodeRange.selectNode(sel);
26850             } catch (e) {
26851                 nodeRange.selectNodeContents(sel);
26852             }
26853             //nodeRange.collapse(true);
26854             var s = editor.win.getSelection();
26855             s.removeAllRanges();
26856             s.addRange(nodeRange);
26857         }  
26858         
26859       
26860         var updateFooter = sel ? false : true;
26861         
26862         
26863         var ans = this.editor.getAllAncestors();
26864         
26865         // pick
26866         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26867         
26868         if (!sel) { 
26869             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
26870             sel = sel ? sel : this.editor.doc.body;
26871             sel = sel.tagName.length ? sel : this.editor.doc.body;
26872             
26873         }
26874         // pick a menu that exists..
26875         var tn = sel.tagName.toUpperCase();
26876         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
26877         
26878         tn = sel.tagName.toUpperCase();
26879         
26880         var lastSel = this.tb.selectedNode
26881         
26882         this.tb.selectedNode = sel;
26883         
26884         // if current menu does not match..
26885         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
26886                 
26887             this.tb.el.hide();
26888             ///console.log("show: " + tn);
26889             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
26890             this.tb.el.show();
26891             // update name
26892             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
26893             
26894             
26895             // update attributes
26896             if (this.tb.fields) {
26897                 this.tb.fields.each(function(e) {
26898                    e.setValue(sel.getAttribute(e.attrname));
26899                 });
26900             }
26901             
26902             var hasStyles = false;
26903             for(var i in this.styles) {
26904                 hasStyles = true;
26905                 break;
26906             }
26907             
26908             // update styles
26909             if (hasStyles) { 
26910                 var st = this.tb.fields.item(0);
26911                 
26912                 st.store.removeAll();
26913                
26914                 
26915                 var cn = sel.className.split(/\s+/);
26916                 
26917                 var avs = [];
26918                 if (this.styles['*']) {
26919                     
26920                     Roo.each(this.styles['*'], function(v) {
26921                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
26922                     });
26923                 }
26924                 if (this.styles[tn]) { 
26925                     Roo.each(this.styles[tn], function(v) {
26926                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
26927                     });
26928                 }
26929                 
26930                 st.store.loadData(avs);
26931                 st.collapse();
26932                 st.setValue(cn);
26933             }
26934             // flag our selected Node.
26935             this.tb.selectedNode = sel;
26936            
26937            
26938             Roo.menu.MenuMgr.hideAll();
26939
26940         }
26941         
26942         if (!updateFooter) {
26943             return;
26944         }
26945         // update the footer
26946         //
26947         var html = '';
26948         
26949         this.footerEls = ans.reverse();
26950         Roo.each(this.footerEls, function(a,i) {
26951             if (!a) { return; }
26952             html += html.length ? ' &gt; '  :  '';
26953             
26954             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
26955             
26956         });
26957        
26958         // 
26959         var sz = this.footDisp.up('td').getSize();
26960         this.footDisp.dom.style.width = (sz.width -10) + 'px';
26961         this.footDisp.dom.style.marginLeft = '5px';
26962         
26963         this.footDisp.dom.style.overflow = 'hidden';
26964         
26965         this.footDisp.dom.innerHTML = html;
26966             
26967         //this.editorsyncValue();
26968     },
26969    
26970        
26971     // private
26972     onDestroy : function(){
26973         if(this.rendered){
26974             
26975             this.tb.items.each(function(item){
26976                 if(item.menu){
26977                     item.menu.removeAll();
26978                     if(item.menu.el){
26979                         item.menu.el.destroy();
26980                     }
26981                 }
26982                 item.destroy();
26983             });
26984              
26985         }
26986     },
26987     onFirstFocus: function() {
26988         // need to do this for all the toolbars..
26989         this.tb.items.each(function(item){
26990            item.enable();
26991         });
26992     },
26993     buildToolbar: function(tlist, nm)
26994     {
26995         var editor = this.editor;
26996          // create a new element.
26997         var wdiv = editor.wrap.createChild({
26998                 tag: 'div'
26999             }, editor.wrap.dom.firstChild.nextSibling, true);
27000         
27001        
27002         var tb = new Roo.Toolbar(wdiv);
27003         // add the name..
27004         
27005         tb.add(nm+ ":&nbsp;");
27006         
27007         var styles = [];
27008         for(var i in this.styles) {
27009             styles.push(i);
27010         }
27011         
27012         // styles...
27013         if (styles && styles.length) {
27014             
27015             // this needs a multi-select checkbox...
27016             tb.addField( new Roo.form.ComboBox({
27017                 store: new Roo.data.SimpleStore({
27018                     id : 'val',
27019                     fields: ['val', 'selected'],
27020                     data : [] 
27021                 }),
27022                 name : '-roo-edit-className',
27023                 attrname : 'className',
27024                 displayField:'val',
27025                 typeAhead: false,
27026                 mode: 'local',
27027                 editable : false,
27028                 triggerAction: 'all',
27029                 emptyText:'Select Style',
27030                 selectOnFocus:true,
27031                 width: 130,
27032                 listeners : {
27033                     'select': function(c, r, i) {
27034                         // initial support only for on class per el..
27035                         tb.selectedNode.className =  r ? r.get('val') : '';
27036                         editor.syncValue();
27037                     }
27038                 }
27039     
27040             }));
27041         }
27042             
27043         
27044         
27045         for (var i in tlist) {
27046             
27047             var item = tlist[i];
27048             tb.add(item.title + ":&nbsp;");
27049             
27050             
27051             
27052             
27053             if (item.opts) {
27054                 // opts == pulldown..
27055                 tb.addField( new Roo.form.ComboBox({
27056                     store: new Roo.data.SimpleStore({
27057                         id : 'val',
27058                         fields: ['val'],
27059                         data : item.opts  
27060                     }),
27061                     name : '-roo-edit-' + i,
27062                     attrname : i,
27063                     displayField:'val',
27064                     typeAhead: false,
27065                     mode: 'local',
27066                     editable : false,
27067                     triggerAction: 'all',
27068                     emptyText:'Select',
27069                     selectOnFocus:true,
27070                     width: item.width ? item.width  : 130,
27071                     listeners : {
27072                         'select': function(c, r, i) {
27073                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27074                         }
27075                     }
27076
27077                 }));
27078                 continue;
27079                     
27080                  
27081                 
27082                 tb.addField( new Roo.form.TextField({
27083                     name: i,
27084                     width: 100,
27085                     //allowBlank:false,
27086                     value: ''
27087                 }));
27088                 continue;
27089             }
27090             tb.addField( new Roo.form.TextField({
27091                 name: '-roo-edit-' + i,
27092                 attrname : i,
27093                 
27094                 width: item.width,
27095                 //allowBlank:true,
27096                 value: '',
27097                 listeners: {
27098                     'change' : function(f, nv, ov) {
27099                         tb.selectedNode.setAttribute(f.attrname, nv);
27100                     }
27101                 }
27102             }));
27103              
27104         }
27105         tb.el.on('click', function(e){
27106             e.preventDefault(); // what does this do?
27107         });
27108         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27109         tb.el.hide();
27110         tb.name = nm;
27111         // dont need to disable them... as they will get hidden
27112         return tb;
27113          
27114         
27115     },
27116     buildFooter : function()
27117     {
27118         
27119         var fel = this.editor.wrap.createChild();
27120         this.footer = new Roo.Toolbar(fel);
27121         // toolbar has scrolly on left / right?
27122         var footDisp= new Roo.Toolbar.Fill();
27123         var _t = this;
27124         this.footer.add(
27125             {
27126                 text : '&lt;',
27127                 xtype: 'Button',
27128                 handler : function() {
27129                     _t.footDisp.scrollTo('left',0,true)
27130                 }
27131             }
27132         );
27133         this.footer.add( footDisp );
27134         this.footer.add( 
27135             {
27136                 text : '&gt;',
27137                 xtype: 'Button',
27138                 handler : function() {
27139                     // no animation..
27140                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27141                 }
27142             }
27143         );
27144         var fel = Roo.get(footDisp.el);
27145         fel.addClass('x-editor-context');
27146         this.footDispWrap = fel; 
27147         this.footDispWrap.overflow  = 'hidden';
27148         
27149         this.footDisp = fel.createChild();
27150         this.footDispWrap.on('click', this.onContextClick, this)
27151         
27152         
27153     },
27154     onContextClick : function (ev,dom)
27155     {
27156         ev.preventDefault();
27157         var  cn = dom.className;
27158         Roo.log(cn);
27159         if (!cn.match(/x-ed-loc-/)) {
27160             return;
27161         }
27162         var n = cn.split('-').pop();
27163         var ans = this.footerEls;
27164         var sel = ans[n];
27165         
27166          // pick
27167         var range = this.editor.createRange();
27168         
27169         range.selectNodeContents(sel);
27170         //range.selectNode(sel);
27171         
27172         
27173         var selection = this.editor.getSelection();
27174         selection.removeAllRanges();
27175         selection.addRange(range);
27176         
27177         
27178         
27179         this.updateToolbar(null, null, sel);
27180         
27181         
27182     }
27183     
27184     
27185     
27186     
27187     
27188 });
27189
27190
27191
27192
27193
27194 /*
27195  * Based on:
27196  * Ext JS Library 1.1.1
27197  * Copyright(c) 2006-2007, Ext JS, LLC.
27198  *
27199  * Originally Released Under LGPL - original licence link has changed is not relivant.
27200  *
27201  * Fork - LGPL
27202  * <script type="text/javascript">
27203  */
27204  
27205 /**
27206  * @class Roo.form.BasicForm
27207  * @extends Roo.util.Observable
27208  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
27209  * @constructor
27210  * @param {String/HTMLElement/Roo.Element} el The form element or its id
27211  * @param {Object} config Configuration options
27212  */
27213 Roo.form.BasicForm = function(el, config){
27214     this.allItems = [];
27215     this.childForms = [];
27216     Roo.apply(this, config);
27217     /*
27218      * The Roo.form.Field items in this form.
27219      * @type MixedCollection
27220      */
27221      
27222      
27223     this.items = new Roo.util.MixedCollection(false, function(o){
27224         return o.id || (o.id = Roo.id());
27225     });
27226     this.addEvents({
27227         /**
27228          * @event beforeaction
27229          * Fires before any action is performed. Return false to cancel the action.
27230          * @param {Form} this
27231          * @param {Action} action The action to be performed
27232          */
27233         beforeaction: true,
27234         /**
27235          * @event actionfailed
27236          * Fires when an action fails.
27237          * @param {Form} this
27238          * @param {Action} action The action that failed
27239          */
27240         actionfailed : true,
27241         /**
27242          * @event actioncomplete
27243          * Fires when an action is completed.
27244          * @param {Form} this
27245          * @param {Action} action The action that completed
27246          */
27247         actioncomplete : true
27248     });
27249     if(el){
27250         this.initEl(el);
27251     }
27252     Roo.form.BasicForm.superclass.constructor.call(this);
27253 };
27254
27255 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
27256     /**
27257      * @cfg {String} method
27258      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
27259      */
27260     /**
27261      * @cfg {DataReader} reader
27262      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
27263      * This is optional as there is built-in support for processing JSON.
27264      */
27265     /**
27266      * @cfg {DataReader} errorReader
27267      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
27268      * This is completely optional as there is built-in support for processing JSON.
27269      */
27270     /**
27271      * @cfg {String} url
27272      * The URL to use for form actions if one isn't supplied in the action options.
27273      */
27274     /**
27275      * @cfg {Boolean} fileUpload
27276      * Set to true if this form is a file upload.
27277      */
27278      
27279     /**
27280      * @cfg {Object} baseParams
27281      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
27282      */
27283      /**
27284      
27285     /**
27286      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
27287      */
27288     timeout: 30,
27289
27290     // private
27291     activeAction : null,
27292
27293     /**
27294      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
27295      * or setValues() data instead of when the form was first created.
27296      */
27297     trackResetOnLoad : false,
27298     
27299     
27300     /**
27301      * childForms - used for multi-tab forms
27302      * @type {Array}
27303      */
27304     childForms : false,
27305     
27306     /**
27307      * allItems - full list of fields.
27308      * @type {Array}
27309      */
27310     allItems : false,
27311     
27312     /**
27313      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
27314      * element by passing it or its id or mask the form itself by passing in true.
27315      * @type Mixed
27316      */
27317     waitMsgTarget : false,
27318
27319     // private
27320     initEl : function(el){
27321         this.el = Roo.get(el);
27322         this.id = this.el.id || Roo.id();
27323         this.el.on('submit', this.onSubmit, this);
27324         this.el.addClass('x-form');
27325     },
27326
27327     // private
27328     onSubmit : function(e){
27329         e.stopEvent();
27330     },
27331
27332     /**
27333      * Returns true if client-side validation on the form is successful.
27334      * @return Boolean
27335      */
27336     isValid : function(){
27337         var valid = true;
27338         this.items.each(function(f){
27339            if(!f.validate()){
27340                valid = false;
27341            }
27342         });
27343         return valid;
27344     },
27345
27346     /**
27347      * Returns true if any fields in this form have changed since their original load.
27348      * @return Boolean
27349      */
27350     isDirty : function(){
27351         var dirty = false;
27352         this.items.each(function(f){
27353            if(f.isDirty()){
27354                dirty = true;
27355                return false;
27356            }
27357         });
27358         return dirty;
27359     },
27360
27361     /**
27362      * Performs a predefined action (submit or load) or custom actions you define on this form.
27363      * @param {String} actionName The name of the action type
27364      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
27365      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
27366      * accept other config options):
27367      * <pre>
27368 Property          Type             Description
27369 ----------------  ---------------  ----------------------------------------------------------------------------------
27370 url               String           The url for the action (defaults to the form's url)
27371 method            String           The form method to use (defaults to the form's method, or POST if not defined)
27372 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
27373 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
27374                                    validate the form on the client (defaults to false)
27375      * </pre>
27376      * @return {BasicForm} this
27377      */
27378     doAction : function(action, options){
27379         if(typeof action == 'string'){
27380             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
27381         }
27382         if(this.fireEvent('beforeaction', this, action) !== false){
27383             this.beforeAction(action);
27384             action.run.defer(100, action);
27385         }
27386         return this;
27387     },
27388
27389     /**
27390      * Shortcut to do a submit action.
27391      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27392      * @return {BasicForm} this
27393      */
27394     submit : function(options){
27395         this.doAction('submit', options);
27396         return this;
27397     },
27398
27399     /**
27400      * Shortcut to do a load action.
27401      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27402      * @return {BasicForm} this
27403      */
27404     load : function(options){
27405         this.doAction('load', options);
27406         return this;
27407     },
27408
27409     /**
27410      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
27411      * @param {Record} record The record to edit
27412      * @return {BasicForm} this
27413      */
27414     updateRecord : function(record){
27415         record.beginEdit();
27416         var fs = record.fields;
27417         fs.each(function(f){
27418             var field = this.findField(f.name);
27419             if(field){
27420                 record.set(f.name, field.getValue());
27421             }
27422         }, this);
27423         record.endEdit();
27424         return this;
27425     },
27426
27427     /**
27428      * Loads an Roo.data.Record into this form.
27429      * @param {Record} record The record to load
27430      * @return {BasicForm} this
27431      */
27432     loadRecord : function(record){
27433         this.setValues(record.data);
27434         return this;
27435     },
27436
27437     // private
27438     beforeAction : function(action){
27439         var o = action.options;
27440         
27441        
27442         if(this.waitMsgTarget === true){
27443             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
27444         }else if(this.waitMsgTarget){
27445             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
27446             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
27447         }else {
27448             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
27449         }
27450          
27451     },
27452
27453     // private
27454     afterAction : function(action, success){
27455         this.activeAction = null;
27456         var o = action.options;
27457         
27458         if(this.waitMsgTarget === true){
27459             this.el.unmask();
27460         }else if(this.waitMsgTarget){
27461             this.waitMsgTarget.unmask();
27462         }else{
27463             Roo.MessageBox.updateProgress(1);
27464             Roo.MessageBox.hide();
27465         }
27466          
27467         if(success){
27468             if(o.reset){
27469                 this.reset();
27470             }
27471             Roo.callback(o.success, o.scope, [this, action]);
27472             this.fireEvent('actioncomplete', this, action);
27473             
27474         }else{
27475             
27476             // failure condition..
27477             // we have a scenario where updates need confirming.
27478             // eg. if a locking scenario exists..
27479             // we look for { errors : { needs_confirm : true }} in the response.
27480             if (
27481                 (typeof(action.result) != 'undefined')  &&
27482                 (typeof(action.result.errors) != 'undefined')  &&
27483                 (typeof(action.result.errors.needs_confirm) != 'undefined')
27484           ){
27485                 var _t = this;
27486                 Roo.MessageBox.confirm(
27487                     "Change requires confirmation",
27488                     action.result.errorMsg,
27489                     function(r) {
27490                         if (r != 'yes') {
27491                             return;
27492                         }
27493                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
27494                     }
27495                     
27496                 );
27497                 
27498                 
27499                 
27500                 return;
27501             }
27502             
27503             Roo.callback(o.failure, o.scope, [this, action]);
27504             // show an error message if no failed handler is set..
27505             if (!this.hasListener('actionfailed')) {
27506                 Roo.MessageBox.alert("Error",
27507                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
27508                         action.result.errorMsg :
27509                         "Saving Failed, please check your entries or try again"
27510                 );
27511             }
27512             
27513             this.fireEvent('actionfailed', this, action);
27514         }
27515         
27516     },
27517
27518     /**
27519      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
27520      * @param {String} id The value to search for
27521      * @return Field
27522      */
27523     findField : function(id){
27524         var field = this.items.get(id);
27525         if(!field){
27526             this.items.each(function(f){
27527                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
27528                     field = f;
27529                     return false;
27530                 }
27531             });
27532         }
27533         return field || null;
27534     },
27535
27536     /**
27537      * Add a secondary form to this one, 
27538      * Used to provide tabbed forms. One form is primary, with hidden values 
27539      * which mirror the elements from the other forms.
27540      * 
27541      * @param {Roo.form.Form} form to add.
27542      * 
27543      */
27544     addForm : function(form)
27545     {
27546        
27547         if (this.childForms.indexOf(form) > -1) {
27548             // already added..
27549             return;
27550         }
27551         this.childForms.push(form);
27552         var n = '';
27553         Roo.each(form.allItems, function (fe) {
27554             
27555             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
27556             if (this.findField(n)) { // already added..
27557                 return;
27558             }
27559             var add = new Roo.form.Hidden({
27560                 name : n
27561             });
27562             add.render(this.el);
27563             
27564             this.add( add );
27565         }, this);
27566         
27567     },
27568     /**
27569      * Mark fields in this form invalid in bulk.
27570      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
27571      * @return {BasicForm} this
27572      */
27573     markInvalid : function(errors){
27574         if(errors instanceof Array){
27575             for(var i = 0, len = errors.length; i < len; i++){
27576                 var fieldError = errors[i];
27577                 var f = this.findField(fieldError.id);
27578                 if(f){
27579                     f.markInvalid(fieldError.msg);
27580                 }
27581             }
27582         }else{
27583             var field, id;
27584             for(id in errors){
27585                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
27586                     field.markInvalid(errors[id]);
27587                 }
27588             }
27589         }
27590         Roo.each(this.childForms || [], function (f) {
27591             f.markInvalid(errors);
27592         });
27593         
27594         return this;
27595     },
27596
27597     /**
27598      * Set values for fields in this form in bulk.
27599      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
27600      * @return {BasicForm} this
27601      */
27602     setValues : function(values){
27603         if(values instanceof Array){ // array of objects
27604             for(var i = 0, len = values.length; i < len; i++){
27605                 var v = values[i];
27606                 var f = this.findField(v.id);
27607                 if(f){
27608                     f.setValue(v.value);
27609                     if(this.trackResetOnLoad){
27610                         f.originalValue = f.getValue();
27611                     }
27612                 }
27613             }
27614         }else{ // object hash
27615             var field, id;
27616             for(id in values){
27617                 if(typeof values[id] != 'function' && (field = this.findField(id))){
27618                     
27619                     if (field.setFromData && 
27620                         field.valueField && 
27621                         field.displayField &&
27622                         // combos' with local stores can 
27623                         // be queried via setValue()
27624                         // to set their value..
27625                         (field.store && !field.store.isLocal)
27626                         ) {
27627                         // it's a combo
27628                         var sd = { };
27629                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
27630                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
27631                         field.setFromData(sd);
27632                         
27633                     } else {
27634                         field.setValue(values[id]);
27635                     }
27636                     
27637                     
27638                     if(this.trackResetOnLoad){
27639                         field.originalValue = field.getValue();
27640                     }
27641                 }
27642             }
27643         }
27644          
27645         Roo.each(this.childForms || [], function (f) {
27646             f.setValues(values);
27647         });
27648                 
27649         return this;
27650     },
27651
27652     /**
27653      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
27654      * they are returned as an array.
27655      * @param {Boolean} asString
27656      * @return {Object}
27657      */
27658     getValues : function(asString){
27659         if (this.childForms) {
27660             // copy values from the child forms
27661             Roo.each(this.childForms, function (f) {
27662                 this.setValues(f.getValues());
27663             }, this);
27664         }
27665         
27666         
27667         
27668         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
27669         if(asString === true){
27670             return fs;
27671         }
27672         return Roo.urlDecode(fs);
27673     },
27674     
27675     /**
27676      * Returns the fields in this form as an object with key/value pairs. 
27677      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
27678      * @return {Object}
27679      */
27680     getFieldValues : function(with_hidden)
27681     {
27682         if (this.childForms) {
27683             // copy values from the child forms
27684             // should this call getFieldValues - probably not as we do not currently copy
27685             // hidden fields when we generate..
27686             Roo.each(this.childForms, function (f) {
27687                 this.setValues(f.getValues());
27688             }, this);
27689         }
27690         
27691         var ret = {};
27692         this.items.each(function(f){
27693             if (!f.getName()) {
27694                 return;
27695             }
27696             var v = f.getValue();
27697             // not sure if this supported any more..
27698             if ((typeof(v) == 'object') && f.getRawValue) {
27699                 v = f.getRawValue() ; // dates..
27700             }
27701             // combo boxes where name != hiddenName...
27702             if (f.name != f.getName()) {
27703                 ret[f.name] = f.getRawValue();
27704             }
27705             ret[f.getName()] = v;
27706         });
27707         
27708         return ret;
27709     },
27710
27711     /**
27712      * Clears all invalid messages in this form.
27713      * @return {BasicForm} this
27714      */
27715     clearInvalid : function(){
27716         this.items.each(function(f){
27717            f.clearInvalid();
27718         });
27719         
27720         Roo.each(this.childForms || [], function (f) {
27721             f.clearInvalid();
27722         });
27723         
27724         
27725         return this;
27726     },
27727
27728     /**
27729      * Resets this form.
27730      * @return {BasicForm} this
27731      */
27732     reset : function(){
27733         this.items.each(function(f){
27734             f.reset();
27735         });
27736         
27737         Roo.each(this.childForms || [], function (f) {
27738             f.reset();
27739         });
27740        
27741         
27742         return this;
27743     },
27744
27745     /**
27746      * Add Roo.form components to this form.
27747      * @param {Field} field1
27748      * @param {Field} field2 (optional)
27749      * @param {Field} etc (optional)
27750      * @return {BasicForm} this
27751      */
27752     add : function(){
27753         this.items.addAll(Array.prototype.slice.call(arguments, 0));
27754         return this;
27755     },
27756
27757
27758     /**
27759      * Removes a field from the items collection (does NOT remove its markup).
27760      * @param {Field} field
27761      * @return {BasicForm} this
27762      */
27763     remove : function(field){
27764         this.items.remove(field);
27765         return this;
27766     },
27767
27768     /**
27769      * Looks at the fields in this form, checks them for an id attribute,
27770      * and calls applyTo on the existing dom element with that id.
27771      * @return {BasicForm} this
27772      */
27773     render : function(){
27774         this.items.each(function(f){
27775             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
27776                 f.applyTo(f.id);
27777             }
27778         });
27779         return this;
27780     },
27781
27782     /**
27783      * Calls {@link Ext#apply} for all fields in this form with the passed object.
27784      * @param {Object} values
27785      * @return {BasicForm} this
27786      */
27787     applyToFields : function(o){
27788         this.items.each(function(f){
27789            Roo.apply(f, o);
27790         });
27791         return this;
27792     },
27793
27794     /**
27795      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
27796      * @param {Object} values
27797      * @return {BasicForm} this
27798      */
27799     applyIfToFields : function(o){
27800         this.items.each(function(f){
27801            Roo.applyIf(f, o);
27802         });
27803         return this;
27804     }
27805 });
27806
27807 // back compat
27808 Roo.BasicForm = Roo.form.BasicForm;/*
27809  * Based on:
27810  * Ext JS Library 1.1.1
27811  * Copyright(c) 2006-2007, Ext JS, LLC.
27812  *
27813  * Originally Released Under LGPL - original licence link has changed is not relivant.
27814  *
27815  * Fork - LGPL
27816  * <script type="text/javascript">
27817  */
27818
27819 /**
27820  * @class Roo.form.Form
27821  * @extends Roo.form.BasicForm
27822  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
27823  * @constructor
27824  * @param {Object} config Configuration options
27825  */
27826 Roo.form.Form = function(config){
27827     var xitems =  [];
27828     if (config.items) {
27829         xitems = config.items;
27830         delete config.items;
27831     }
27832    
27833     
27834     Roo.form.Form.superclass.constructor.call(this, null, config);
27835     this.url = this.url || this.action;
27836     if(!this.root){
27837         this.root = new Roo.form.Layout(Roo.applyIf({
27838             id: Roo.id()
27839         }, config));
27840     }
27841     this.active = this.root;
27842     /**
27843      * Array of all the buttons that have been added to this form via {@link addButton}
27844      * @type Array
27845      */
27846     this.buttons = [];
27847     this.allItems = [];
27848     this.addEvents({
27849         /**
27850          * @event clientvalidation
27851          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
27852          * @param {Form} this
27853          * @param {Boolean} valid true if the form has passed client-side validation
27854          */
27855         clientvalidation: true,
27856         /**
27857          * @event rendered
27858          * Fires when the form is rendered
27859          * @param {Roo.form.Form} form
27860          */
27861         rendered : true
27862     });
27863     
27864     if (this.progressUrl) {
27865             // push a hidden field onto the list of fields..
27866             this.addxtype( {
27867                     xns: Roo.form, 
27868                     xtype : 'Hidden', 
27869                     name : 'UPLOAD_IDENTIFIER' 
27870             });
27871         }
27872         
27873     
27874     Roo.each(xitems, this.addxtype, this);
27875     
27876     
27877     
27878 };
27879
27880 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
27881     /**
27882      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
27883      */
27884     /**
27885      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
27886      */
27887     /**
27888      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
27889      */
27890     buttonAlign:'center',
27891
27892     /**
27893      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
27894      */
27895     minButtonWidth:75,
27896
27897     /**
27898      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
27899      * This property cascades to child containers if not set.
27900      */
27901     labelAlign:'left',
27902
27903     /**
27904      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
27905      * fires a looping event with that state. This is required to bind buttons to the valid
27906      * state using the config value formBind:true on the button.
27907      */
27908     monitorValid : false,
27909
27910     /**
27911      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
27912      */
27913     monitorPoll : 200,
27914     
27915     /**
27916      * @cfg {String} progressUrl - Url to return progress data 
27917      */
27918     
27919     progressUrl : false,
27920   
27921     /**
27922      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
27923      * fields are added and the column is closed. If no fields are passed the column remains open
27924      * until end() is called.
27925      * @param {Object} config The config to pass to the column
27926      * @param {Field} field1 (optional)
27927      * @param {Field} field2 (optional)
27928      * @param {Field} etc (optional)
27929      * @return Column The column container object
27930      */
27931     column : function(c){
27932         var col = new Roo.form.Column(c);
27933         this.start(col);
27934         if(arguments.length > 1){ // duplicate code required because of Opera
27935             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27936             this.end();
27937         }
27938         return col;
27939     },
27940
27941     /**
27942      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
27943      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
27944      * until end() is called.
27945      * @param {Object} config The config to pass to the fieldset
27946      * @param {Field} field1 (optional)
27947      * @param {Field} field2 (optional)
27948      * @param {Field} etc (optional)
27949      * @return FieldSet The fieldset container object
27950      */
27951     fieldset : function(c){
27952         var fs = new Roo.form.FieldSet(c);
27953         this.start(fs);
27954         if(arguments.length > 1){ // duplicate code required because of Opera
27955             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27956             this.end();
27957         }
27958         return fs;
27959     },
27960
27961     /**
27962      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
27963      * fields are added and the container is closed. If no fields are passed the container remains open
27964      * until end() is called.
27965      * @param {Object} config The config to pass to the Layout
27966      * @param {Field} field1 (optional)
27967      * @param {Field} field2 (optional)
27968      * @param {Field} etc (optional)
27969      * @return Layout The container object
27970      */
27971     container : function(c){
27972         var l = new Roo.form.Layout(c);
27973         this.start(l);
27974         if(arguments.length > 1){ // duplicate code required because of Opera
27975             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27976             this.end();
27977         }
27978         return l;
27979     },
27980
27981     /**
27982      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
27983      * @param {Object} container A Roo.form.Layout or subclass of Layout
27984      * @return {Form} this
27985      */
27986     start : function(c){
27987         // cascade label info
27988         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
27989         this.active.stack.push(c);
27990         c.ownerCt = this.active;
27991         this.active = c;
27992         return this;
27993     },
27994
27995     /**
27996      * Closes the current open container
27997      * @return {Form} this
27998      */
27999     end : function(){
28000         if(this.active == this.root){
28001             return this;
28002         }
28003         this.active = this.active.ownerCt;
28004         return this;
28005     },
28006
28007     /**
28008      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28009      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28010      * as the label of the field.
28011      * @param {Field} field1
28012      * @param {Field} field2 (optional)
28013      * @param {Field} etc. (optional)
28014      * @return {Form} this
28015      */
28016     add : function(){
28017         this.active.stack.push.apply(this.active.stack, arguments);
28018         this.allItems.push.apply(this.allItems,arguments);
28019         var r = [];
28020         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28021             if(a[i].isFormField){
28022                 r.push(a[i]);
28023             }
28024         }
28025         if(r.length > 0){
28026             Roo.form.Form.superclass.add.apply(this, r);
28027         }
28028         return this;
28029     },
28030     
28031
28032     
28033     
28034     
28035      /**
28036      * Find any element that has been added to a form, using it's ID or name
28037      * This can include framesets, columns etc. along with regular fields..
28038      * @param {String} id - id or name to find.
28039      
28040      * @return {Element} e - or false if nothing found.
28041      */
28042     findbyId : function(id)
28043     {
28044         var ret = false;
28045         if (!id) {
28046             return ret;
28047         }
28048         Roo.each(this.allItems, function(f){
28049             if (f.id == id || f.name == id ){
28050                 ret = f;
28051                 return false;
28052             }
28053         });
28054         return ret;
28055     },
28056
28057     
28058     
28059     /**
28060      * Render this form into the passed container. This should only be called once!
28061      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28062      * @return {Form} this
28063      */
28064     render : function(ct)
28065     {
28066         
28067         
28068         
28069         ct = Roo.get(ct);
28070         var o = this.autoCreate || {
28071             tag: 'form',
28072             method : this.method || 'POST',
28073             id : this.id || Roo.id()
28074         };
28075         this.initEl(ct.createChild(o));
28076
28077         this.root.render(this.el);
28078         
28079        
28080              
28081         this.items.each(function(f){
28082             f.render('x-form-el-'+f.id);
28083         });
28084
28085         if(this.buttons.length > 0){
28086             // tables are required to maintain order and for correct IE layout
28087             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28088                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28089                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28090             }}, null, true);
28091             var tr = tb.getElementsByTagName('tr')[0];
28092             for(var i = 0, len = this.buttons.length; i < len; i++) {
28093                 var b = this.buttons[i];
28094                 var td = document.createElement('td');
28095                 td.className = 'x-form-btn-td';
28096                 b.render(tr.appendChild(td));
28097             }
28098         }
28099         if(this.monitorValid){ // initialize after render
28100             this.startMonitoring();
28101         }
28102         this.fireEvent('rendered', this);
28103         return this;
28104     },
28105
28106     /**
28107      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28108      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28109      * object or a valid Roo.DomHelper element config
28110      * @param {Function} handler The function called when the button is clicked
28111      * @param {Object} scope (optional) The scope of the handler function
28112      * @return {Roo.Button}
28113      */
28114     addButton : function(config, handler, scope){
28115         var bc = {
28116             handler: handler,
28117             scope: scope,
28118             minWidth: this.minButtonWidth,
28119             hideParent:true
28120         };
28121         if(typeof config == "string"){
28122             bc.text = config;
28123         }else{
28124             Roo.apply(bc, config);
28125         }
28126         var btn = new Roo.Button(null, bc);
28127         this.buttons.push(btn);
28128         return btn;
28129     },
28130
28131      /**
28132      * Adds a series of form elements (using the xtype property as the factory method.
28133      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28134      * @param {Object} config 
28135      */
28136     
28137     addxtype : function()
28138     {
28139         var ar = Array.prototype.slice.call(arguments, 0);
28140         var ret = false;
28141         for(var i = 0; i < ar.length; i++) {
28142             if (!ar[i]) {
28143                 continue; // skip -- if this happends something invalid got sent, we 
28144                 // should ignore it, as basically that interface element will not show up
28145                 // and that should be pretty obvious!!
28146             }
28147             
28148             if (Roo.form[ar[i].xtype]) {
28149                 ar[i].form = this;
28150                 var fe = Roo.factory(ar[i], Roo.form);
28151                 if (!ret) {
28152                     ret = fe;
28153                 }
28154                 fe.form = this;
28155                 if (fe.store) {
28156                     fe.store.form = this;
28157                 }
28158                 if (fe.isLayout) {  
28159                          
28160                     this.start(fe);
28161                     this.allItems.push(fe);
28162                     if (fe.items && fe.addxtype) {
28163                         fe.addxtype.apply(fe, fe.items);
28164                         delete fe.items;
28165                     }
28166                      this.end();
28167                     continue;
28168                 }
28169                 
28170                 
28171                  
28172                 this.add(fe);
28173               //  console.log('adding ' + ar[i].xtype);
28174             }
28175             if (ar[i].xtype == 'Button') {  
28176                 //console.log('adding button');
28177                 //console.log(ar[i]);
28178                 this.addButton(ar[i]);
28179                 this.allItems.push(fe);
28180                 continue;
28181             }
28182             
28183             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
28184                 alert('end is not supported on xtype any more, use items');
28185             //    this.end();
28186             //    //console.log('adding end');
28187             }
28188             
28189         }
28190         return ret;
28191     },
28192     
28193     /**
28194      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
28195      * option "monitorValid"
28196      */
28197     startMonitoring : function(){
28198         if(!this.bound){
28199             this.bound = true;
28200             Roo.TaskMgr.start({
28201                 run : this.bindHandler,
28202                 interval : this.monitorPoll || 200,
28203                 scope: this
28204             });
28205         }
28206     },
28207
28208     /**
28209      * Stops monitoring of the valid state of this form
28210      */
28211     stopMonitoring : function(){
28212         this.bound = false;
28213     },
28214
28215     // private
28216     bindHandler : function(){
28217         if(!this.bound){
28218             return false; // stops binding
28219         }
28220         var valid = true;
28221         this.items.each(function(f){
28222             if(!f.isValid(true)){
28223                 valid = false;
28224                 return false;
28225             }
28226         });
28227         for(var i = 0, len = this.buttons.length; i < len; i++){
28228             var btn = this.buttons[i];
28229             if(btn.formBind === true && btn.disabled === valid){
28230                 btn.setDisabled(!valid);
28231             }
28232         }
28233         this.fireEvent('clientvalidation', this, valid);
28234     }
28235     
28236     
28237     
28238     
28239     
28240     
28241     
28242     
28243 });
28244
28245
28246 // back compat
28247 Roo.Form = Roo.form.Form;
28248 /*
28249  * Based on:
28250  * Ext JS Library 1.1.1
28251  * Copyright(c) 2006-2007, Ext JS, LLC.
28252  *
28253  * Originally Released Under LGPL - original licence link has changed is not relivant.
28254  *
28255  * Fork - LGPL
28256  * <script type="text/javascript">
28257  */
28258  
28259  /**
28260  * @class Roo.form.Action
28261  * Internal Class used to handle form actions
28262  * @constructor
28263  * @param {Roo.form.BasicForm} el The form element or its id
28264  * @param {Object} config Configuration options
28265  */
28266  
28267  
28268 // define the action interface
28269 Roo.form.Action = function(form, options){
28270     this.form = form;
28271     this.options = options || {};
28272 };
28273 /**
28274  * Client Validation Failed
28275  * @const 
28276  */
28277 Roo.form.Action.CLIENT_INVALID = 'client';
28278 /**
28279  * Server Validation Failed
28280  * @const 
28281  */
28282  Roo.form.Action.SERVER_INVALID = 'server';
28283  /**
28284  * Connect to Server Failed
28285  * @const 
28286  */
28287 Roo.form.Action.CONNECT_FAILURE = 'connect';
28288 /**
28289  * Reading Data from Server Failed
28290  * @const 
28291  */
28292 Roo.form.Action.LOAD_FAILURE = 'load';
28293
28294 Roo.form.Action.prototype = {
28295     type : 'default',
28296     failureType : undefined,
28297     response : undefined,
28298     result : undefined,
28299
28300     // interface method
28301     run : function(options){
28302
28303     },
28304
28305     // interface method
28306     success : function(response){
28307
28308     },
28309
28310     // interface method
28311     handleResponse : function(response){
28312
28313     },
28314
28315     // default connection failure
28316     failure : function(response){
28317         
28318         this.response = response;
28319         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28320         this.form.afterAction(this, false);
28321     },
28322
28323     processResponse : function(response){
28324         this.response = response;
28325         if(!response.responseText){
28326             return true;
28327         }
28328         this.result = this.handleResponse(response);
28329         return this.result;
28330     },
28331
28332     // utility functions used internally
28333     getUrl : function(appendParams){
28334         var url = this.options.url || this.form.url || this.form.el.dom.action;
28335         if(appendParams){
28336             var p = this.getParams();
28337             if(p){
28338                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
28339             }
28340         }
28341         return url;
28342     },
28343
28344     getMethod : function(){
28345         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
28346     },
28347
28348     getParams : function(){
28349         var bp = this.form.baseParams;
28350         var p = this.options.params;
28351         if(p){
28352             if(typeof p == "object"){
28353                 p = Roo.urlEncode(Roo.applyIf(p, bp));
28354             }else if(typeof p == 'string' && bp){
28355                 p += '&' + Roo.urlEncode(bp);
28356             }
28357         }else if(bp){
28358             p = Roo.urlEncode(bp);
28359         }
28360         return p;
28361     },
28362
28363     createCallback : function(){
28364         return {
28365             success: this.success,
28366             failure: this.failure,
28367             scope: this,
28368             timeout: (this.form.timeout*1000),
28369             upload: this.form.fileUpload ? this.success : undefined
28370         };
28371     }
28372 };
28373
28374 Roo.form.Action.Submit = function(form, options){
28375     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
28376 };
28377
28378 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
28379     type : 'submit',
28380
28381     haveProgress : false,
28382     uploadComplete : false,
28383     
28384     // uploadProgress indicator.
28385     uploadProgress : function()
28386     {
28387         if (!this.form.progressUrl) {
28388             return;
28389         }
28390         
28391         if (!this.haveProgress) {
28392             Roo.MessageBox.progress("Uploading", "Uploading");
28393         }
28394         if (this.uploadComplete) {
28395            Roo.MessageBox.hide();
28396            return;
28397         }
28398         
28399         this.haveProgress = true;
28400    
28401         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
28402         
28403         var c = new Roo.data.Connection();
28404         c.request({
28405             url : this.form.progressUrl,
28406             params: {
28407                 id : uid
28408             },
28409             method: 'GET',
28410             success : function(req){
28411                //console.log(data);
28412                 var rdata = false;
28413                 var edata;
28414                 try  {
28415                    rdata = Roo.decode(req.responseText)
28416                 } catch (e) {
28417                     Roo.log("Invalid data from server..");
28418                     Roo.log(edata);
28419                     return;
28420                 }
28421                 if (!rdata || !rdata.success) {
28422                     Roo.log(rdata);
28423                     return;
28424                 }
28425                 var data = rdata.data;
28426                 
28427                 if (this.uploadComplete) {
28428                    Roo.MessageBox.hide();
28429                    return;
28430                 }
28431                    
28432                 if (data){
28433                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
28434                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
28435                     );
28436                 }
28437                 this.uploadProgress.defer(2000,this);
28438             },
28439        
28440             failure: function(data) {
28441                 Roo.log('progress url failed ');
28442                 Roo.log(data);
28443             },
28444             scope : this
28445         });
28446            
28447     },
28448     
28449     
28450     run : function()
28451     {
28452         // run get Values on the form, so it syncs any secondary forms.
28453         this.form.getValues();
28454         
28455         var o = this.options;
28456         var method = this.getMethod();
28457         var isPost = method == 'POST';
28458         if(o.clientValidation === false || this.form.isValid()){
28459             
28460             if (this.form.progressUrl) {
28461                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
28462                     (new Date() * 1) + '' + Math.random());
28463                     
28464             } 
28465             
28466             
28467             Roo.Ajax.request(Roo.apply(this.createCallback(), {
28468                 form:this.form.el.dom,
28469                 url:this.getUrl(!isPost),
28470                 method: method,
28471                 params:isPost ? this.getParams() : null,
28472                 isUpload: this.form.fileUpload
28473             }));
28474             
28475             this.uploadProgress();
28476
28477         }else if (o.clientValidation !== false){ // client validation failed
28478             this.failureType = Roo.form.Action.CLIENT_INVALID;
28479             this.form.afterAction(this, false);
28480         }
28481     },
28482
28483     success : function(response)
28484     {
28485         this.uploadComplete= true;
28486         if (this.haveProgress) {
28487             Roo.MessageBox.hide();
28488         }
28489         
28490         
28491         var result = this.processResponse(response);
28492         if(result === true || result.success){
28493             this.form.afterAction(this, true);
28494             return;
28495         }
28496         if(result.errors){
28497             this.form.markInvalid(result.errors);
28498             this.failureType = Roo.form.Action.SERVER_INVALID;
28499         }
28500         this.form.afterAction(this, false);
28501     },
28502     failure : function(response)
28503     {
28504         this.uploadComplete= true;
28505         if (this.haveProgress) {
28506             Roo.MessageBox.hide();
28507         }
28508         
28509         this.response = response;
28510         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28511         this.form.afterAction(this, false);
28512     },
28513     
28514     handleResponse : function(response){
28515         if(this.form.errorReader){
28516             var rs = this.form.errorReader.read(response);
28517             var errors = [];
28518             if(rs.records){
28519                 for(var i = 0, len = rs.records.length; i < len; i++) {
28520                     var r = rs.records[i];
28521                     errors[i] = r.data;
28522                 }
28523             }
28524             if(errors.length < 1){
28525                 errors = null;
28526             }
28527             return {
28528                 success : rs.success,
28529                 errors : errors
28530             };
28531         }
28532         var ret = false;
28533         try {
28534             ret = Roo.decode(response.responseText);
28535         } catch (e) {
28536             ret = {
28537                 success: false,
28538                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
28539                 errors : []
28540             };
28541         }
28542         return ret;
28543         
28544     }
28545 });
28546
28547
28548 Roo.form.Action.Load = function(form, options){
28549     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
28550     this.reader = this.form.reader;
28551 };
28552
28553 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
28554     type : 'load',
28555
28556     run : function(){
28557         
28558         Roo.Ajax.request(Roo.apply(
28559                 this.createCallback(), {
28560                     method:this.getMethod(),
28561                     url:this.getUrl(false),
28562                     params:this.getParams()
28563         }));
28564     },
28565
28566     success : function(response){
28567         
28568         var result = this.processResponse(response);
28569         if(result === true || !result.success || !result.data){
28570             this.failureType = Roo.form.Action.LOAD_FAILURE;
28571             this.form.afterAction(this, false);
28572             return;
28573         }
28574         this.form.clearInvalid();
28575         this.form.setValues(result.data);
28576         this.form.afterAction(this, true);
28577     },
28578
28579     handleResponse : function(response){
28580         if(this.form.reader){
28581             var rs = this.form.reader.read(response);
28582             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
28583             return {
28584                 success : rs.success,
28585                 data : data
28586             };
28587         }
28588         return Roo.decode(response.responseText);
28589     }
28590 });
28591
28592 Roo.form.Action.ACTION_TYPES = {
28593     'load' : Roo.form.Action.Load,
28594     'submit' : Roo.form.Action.Submit
28595 };/*
28596  * Based on:
28597  * Ext JS Library 1.1.1
28598  * Copyright(c) 2006-2007, Ext JS, LLC.
28599  *
28600  * Originally Released Under LGPL - original licence link has changed is not relivant.
28601  *
28602  * Fork - LGPL
28603  * <script type="text/javascript">
28604  */
28605  
28606 /**
28607  * @class Roo.form.Layout
28608  * @extends Roo.Component
28609  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
28610  * @constructor
28611  * @param {Object} config Configuration options
28612  */
28613 Roo.form.Layout = function(config){
28614     var xitems = [];
28615     if (config.items) {
28616         xitems = config.items;
28617         delete config.items;
28618     }
28619     Roo.form.Layout.superclass.constructor.call(this, config);
28620     this.stack = [];
28621     Roo.each(xitems, this.addxtype, this);
28622      
28623 };
28624
28625 Roo.extend(Roo.form.Layout, Roo.Component, {
28626     /**
28627      * @cfg {String/Object} autoCreate
28628      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
28629      */
28630     /**
28631      * @cfg {String/Object/Function} style
28632      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
28633      * a function which returns such a specification.
28634      */
28635     /**
28636      * @cfg {String} labelAlign
28637      * Valid values are "left," "top" and "right" (defaults to "left")
28638      */
28639     /**
28640      * @cfg {Number} labelWidth
28641      * Fixed width in pixels of all field labels (defaults to undefined)
28642      */
28643     /**
28644      * @cfg {Boolean} clear
28645      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
28646      */
28647     clear : true,
28648     /**
28649      * @cfg {String} labelSeparator
28650      * The separator to use after field labels (defaults to ':')
28651      */
28652     labelSeparator : ':',
28653     /**
28654      * @cfg {Boolean} hideLabels
28655      * True to suppress the display of field labels in this layout (defaults to false)
28656      */
28657     hideLabels : false,
28658
28659     // private
28660     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
28661     
28662     isLayout : true,
28663     
28664     // private
28665     onRender : function(ct, position){
28666         if(this.el){ // from markup
28667             this.el = Roo.get(this.el);
28668         }else {  // generate
28669             var cfg = this.getAutoCreate();
28670             this.el = ct.createChild(cfg, position);
28671         }
28672         if(this.style){
28673             this.el.applyStyles(this.style);
28674         }
28675         if(this.labelAlign){
28676             this.el.addClass('x-form-label-'+this.labelAlign);
28677         }
28678         if(this.hideLabels){
28679             this.labelStyle = "display:none";
28680             this.elementStyle = "padding-left:0;";
28681         }else{
28682             if(typeof this.labelWidth == 'number'){
28683                 this.labelStyle = "width:"+this.labelWidth+"px;";
28684                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
28685             }
28686             if(this.labelAlign == 'top'){
28687                 this.labelStyle = "width:auto;";
28688                 this.elementStyle = "padding-left:0;";
28689             }
28690         }
28691         var stack = this.stack;
28692         var slen = stack.length;
28693         if(slen > 0){
28694             if(!this.fieldTpl){
28695                 var t = new Roo.Template(
28696                     '<div class="x-form-item {5}">',
28697                         '<label for="{0}" style="{2}">{1}{4}</label>',
28698                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28699                         '</div>',
28700                     '</div><div class="x-form-clear-left"></div>'
28701                 );
28702                 t.disableFormats = true;
28703                 t.compile();
28704                 Roo.form.Layout.prototype.fieldTpl = t;
28705             }
28706             for(var i = 0; i < slen; i++) {
28707                 if(stack[i].isFormField){
28708                     this.renderField(stack[i]);
28709                 }else{
28710                     this.renderComponent(stack[i]);
28711                 }
28712             }
28713         }
28714         if(this.clear){
28715             this.el.createChild({cls:'x-form-clear'});
28716         }
28717     },
28718
28719     // private
28720     renderField : function(f){
28721         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
28722                f.id, //0
28723                f.fieldLabel, //1
28724                f.labelStyle||this.labelStyle||'', //2
28725                this.elementStyle||'', //3
28726                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
28727                f.itemCls||this.itemCls||''  //5
28728        ], true).getPrevSibling());
28729     },
28730
28731     // private
28732     renderComponent : function(c){
28733         c.render(c.isLayout ? this.el : this.el.createChild());    
28734     },
28735     /**
28736      * Adds a object form elements (using the xtype property as the factory method.)
28737      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
28738      * @param {Object} config 
28739      */
28740     addxtype : function(o)
28741     {
28742         // create the lement.
28743         o.form = this.form;
28744         var fe = Roo.factory(o, Roo.form);
28745         this.form.allItems.push(fe);
28746         this.stack.push(fe);
28747         
28748         if (fe.isFormField) {
28749             this.form.items.add(fe);
28750         }
28751          
28752         return fe;
28753     }
28754 });
28755
28756 /**
28757  * @class Roo.form.Column
28758  * @extends Roo.form.Layout
28759  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
28760  * @constructor
28761  * @param {Object} config Configuration options
28762  */
28763 Roo.form.Column = function(config){
28764     Roo.form.Column.superclass.constructor.call(this, config);
28765 };
28766
28767 Roo.extend(Roo.form.Column, Roo.form.Layout, {
28768     /**
28769      * @cfg {Number/String} width
28770      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28771      */
28772     /**
28773      * @cfg {String/Object} autoCreate
28774      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
28775      */
28776
28777     // private
28778     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
28779
28780     // private
28781     onRender : function(ct, position){
28782         Roo.form.Column.superclass.onRender.call(this, ct, position);
28783         if(this.width){
28784             this.el.setWidth(this.width);
28785         }
28786     }
28787 });
28788
28789
28790 /**
28791  * @class Roo.form.Row
28792  * @extends Roo.form.Layout
28793  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
28794  * @constructor
28795  * @param {Object} config Configuration options
28796  */
28797
28798  
28799 Roo.form.Row = function(config){
28800     Roo.form.Row.superclass.constructor.call(this, config);
28801 };
28802  
28803 Roo.extend(Roo.form.Row, Roo.form.Layout, {
28804       /**
28805      * @cfg {Number/String} width
28806      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28807      */
28808     /**
28809      * @cfg {Number/String} height
28810      * The fixed height of the column in pixels or CSS value (defaults to "auto")
28811      */
28812     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
28813     
28814     padWidth : 20,
28815     // private
28816     onRender : function(ct, position){
28817         //console.log('row render');
28818         if(!this.rowTpl){
28819             var t = new Roo.Template(
28820                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
28821                     '<label for="{0}" style="{2}">{1}{4}</label>',
28822                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28823                     '</div>',
28824                 '</div>'
28825             );
28826             t.disableFormats = true;
28827             t.compile();
28828             Roo.form.Layout.prototype.rowTpl = t;
28829         }
28830         this.fieldTpl = this.rowTpl;
28831         
28832         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
28833         var labelWidth = 100;
28834         
28835         if ((this.labelAlign != 'top')) {
28836             if (typeof this.labelWidth == 'number') {
28837                 labelWidth = this.labelWidth
28838             }
28839             this.padWidth =  20 + labelWidth;
28840             
28841         }
28842         
28843         Roo.form.Column.superclass.onRender.call(this, ct, position);
28844         if(this.width){
28845             this.el.setWidth(this.width);
28846         }
28847         if(this.height){
28848             this.el.setHeight(this.height);
28849         }
28850     },
28851     
28852     // private
28853     renderField : function(f){
28854         f.fieldEl = this.fieldTpl.append(this.el, [
28855                f.id, f.fieldLabel,
28856                f.labelStyle||this.labelStyle||'',
28857                this.elementStyle||'',
28858                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
28859                f.itemCls||this.itemCls||'',
28860                f.width ? f.width + this.padWidth : 160 + this.padWidth
28861        ],true);
28862     }
28863 });
28864  
28865
28866 /**
28867  * @class Roo.form.FieldSet
28868  * @extends Roo.form.Layout
28869  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
28870  * @constructor
28871  * @param {Object} config Configuration options
28872  */
28873 Roo.form.FieldSet = function(config){
28874     Roo.form.FieldSet.superclass.constructor.call(this, config);
28875 };
28876
28877 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
28878     /**
28879      * @cfg {String} legend
28880      * The text to display as the legend for the FieldSet (defaults to '')
28881      */
28882     /**
28883      * @cfg {String/Object} autoCreate
28884      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
28885      */
28886
28887     // private
28888     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
28889
28890     // private
28891     onRender : function(ct, position){
28892         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
28893         if(this.legend){
28894             this.setLegend(this.legend);
28895         }
28896     },
28897
28898     // private
28899     setLegend : function(text){
28900         if(this.rendered){
28901             this.el.child('legend').update(text);
28902         }
28903     }
28904 });/*
28905  * Based on:
28906  * Ext JS Library 1.1.1
28907  * Copyright(c) 2006-2007, Ext JS, LLC.
28908  *
28909  * Originally Released Under LGPL - original licence link has changed is not relivant.
28910  *
28911  * Fork - LGPL
28912  * <script type="text/javascript">
28913  */
28914 /**
28915  * @class Roo.form.VTypes
28916  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
28917  * @singleton
28918  */
28919 Roo.form.VTypes = function(){
28920     // closure these in so they are only created once.
28921     var alpha = /^[a-zA-Z_]+$/;
28922     var alphanum = /^[a-zA-Z0-9_]+$/;
28923     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
28924     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
28925
28926     // All these messages and functions are configurable
28927     return {
28928         /**
28929          * The function used to validate email addresses
28930          * @param {String} value The email address
28931          */
28932         'email' : function(v){
28933             return email.test(v);
28934         },
28935         /**
28936          * The error text to display when the email validation function returns false
28937          * @type String
28938          */
28939         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
28940         /**
28941          * The keystroke filter mask to be applied on email input
28942          * @type RegExp
28943          */
28944         'emailMask' : /[a-z0-9_\.\-@]/i,
28945
28946         /**
28947          * The function used to validate URLs
28948          * @param {String} value The URL
28949          */
28950         'url' : function(v){
28951             return url.test(v);
28952         },
28953         /**
28954          * The error text to display when the url validation function returns false
28955          * @type String
28956          */
28957         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
28958         
28959         /**
28960          * The function used to validate alpha values
28961          * @param {String} value The value
28962          */
28963         'alpha' : function(v){
28964             return alpha.test(v);
28965         },
28966         /**
28967          * The error text to display when the alpha validation function returns false
28968          * @type String
28969          */
28970         'alphaText' : 'This field should only contain letters and _',
28971         /**
28972          * The keystroke filter mask to be applied on alpha input
28973          * @type RegExp
28974          */
28975         'alphaMask' : /[a-z_]/i,
28976
28977         /**
28978          * The function used to validate alphanumeric values
28979          * @param {String} value The value
28980          */
28981         'alphanum' : function(v){
28982             return alphanum.test(v);
28983         },
28984         /**
28985          * The error text to display when the alphanumeric validation function returns false
28986          * @type String
28987          */
28988         'alphanumText' : 'This field should only contain letters, numbers and _',
28989         /**
28990          * The keystroke filter mask to be applied on alphanumeric input
28991          * @type RegExp
28992          */
28993         'alphanumMask' : /[a-z0-9_]/i
28994     };
28995 }();//<script type="text/javascript">
28996
28997 /**
28998  * @class Roo.form.FCKeditor
28999  * @extends Roo.form.TextArea
29000  * Wrapper around the FCKEditor http://www.fckeditor.net
29001  * @constructor
29002  * Creates a new FCKeditor
29003  * @param {Object} config Configuration options
29004  */
29005 Roo.form.FCKeditor = function(config){
29006     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29007     this.addEvents({
29008          /**
29009          * @event editorinit
29010          * Fired when the editor is initialized - you can add extra handlers here..
29011          * @param {FCKeditor} this
29012          * @param {Object} the FCK object.
29013          */
29014         editorinit : true
29015     });
29016     
29017     
29018 };
29019 Roo.form.FCKeditor.editors = { };
29020 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29021 {
29022     //defaultAutoCreate : {
29023     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29024     //},
29025     // private
29026     /**
29027      * @cfg {Object} fck options - see fck manual for details.
29028      */
29029     fckconfig : false,
29030     
29031     /**
29032      * @cfg {Object} fck toolbar set (Basic or Default)
29033      */
29034     toolbarSet : 'Basic',
29035     /**
29036      * @cfg {Object} fck BasePath
29037      */ 
29038     basePath : '/fckeditor/',
29039     
29040     
29041     frame : false,
29042     
29043     value : '',
29044     
29045    
29046     onRender : function(ct, position)
29047     {
29048         if(!this.el){
29049             this.defaultAutoCreate = {
29050                 tag: "textarea",
29051                 style:"width:300px;height:60px;",
29052                 autocomplete: "off"
29053             };
29054         }
29055         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29056         /*
29057         if(this.grow){
29058             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29059             if(this.preventScrollbars){
29060                 this.el.setStyle("overflow", "hidden");
29061             }
29062             this.el.setHeight(this.growMin);
29063         }
29064         */
29065         //console.log('onrender' + this.getId() );
29066         Roo.form.FCKeditor.editors[this.getId()] = this;
29067          
29068
29069         this.replaceTextarea() ;
29070         
29071     },
29072     
29073     getEditor : function() {
29074         return this.fckEditor;
29075     },
29076     /**
29077      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29078      * @param {Mixed} value The value to set
29079      */
29080     
29081     
29082     setValue : function(value)
29083     {
29084         //console.log('setValue: ' + value);
29085         
29086         if(typeof(value) == 'undefined') { // not sure why this is happending...
29087             return;
29088         }
29089         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29090         
29091         //if(!this.el || !this.getEditor()) {
29092         //    this.value = value;
29093             //this.setValue.defer(100,this,[value]);    
29094         //    return;
29095         //} 
29096         
29097         if(!this.getEditor()) {
29098             return;
29099         }
29100         
29101         this.getEditor().SetData(value);
29102         
29103         //
29104
29105     },
29106
29107     /**
29108      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29109      * @return {Mixed} value The field value
29110      */
29111     getValue : function()
29112     {
29113         
29114         if (this.frame && this.frame.dom.style.display == 'none') {
29115             return Roo.form.FCKeditor.superclass.getValue.call(this);
29116         }
29117         
29118         if(!this.el || !this.getEditor()) {
29119            
29120            // this.getValue.defer(100,this); 
29121             return this.value;
29122         }
29123        
29124         
29125         var value=this.getEditor().GetData();
29126         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29127         return Roo.form.FCKeditor.superclass.getValue.call(this);
29128         
29129
29130     },
29131
29132     /**
29133      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
29134      * @return {Mixed} value The field value
29135      */
29136     getRawValue : function()
29137     {
29138         if (this.frame && this.frame.dom.style.display == 'none') {
29139             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29140         }
29141         
29142         if(!this.el || !this.getEditor()) {
29143             //this.getRawValue.defer(100,this); 
29144             return this.value;
29145             return;
29146         }
29147         
29148         
29149         
29150         var value=this.getEditor().GetData();
29151         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29152         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29153          
29154     },
29155     
29156     setSize : function(w,h) {
29157         
29158         
29159         
29160         //if (this.frame && this.frame.dom.style.display == 'none') {
29161         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29162         //    return;
29163         //}
29164         //if(!this.el || !this.getEditor()) {
29165         //    this.setSize.defer(100,this, [w,h]); 
29166         //    return;
29167         //}
29168         
29169         
29170         
29171         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29172         
29173         this.frame.dom.setAttribute('width', w);
29174         this.frame.dom.setAttribute('height', h);
29175         this.frame.setSize(w,h);
29176         
29177     },
29178     
29179     toggleSourceEdit : function(value) {
29180         
29181       
29182          
29183         this.el.dom.style.display = value ? '' : 'none';
29184         this.frame.dom.style.display = value ?  'none' : '';
29185         
29186     },
29187     
29188     
29189     focus: function(tag)
29190     {
29191         if (this.frame.dom.style.display == 'none') {
29192             return Roo.form.FCKeditor.superclass.focus.call(this);
29193         }
29194         if(!this.el || !this.getEditor()) {
29195             this.focus.defer(100,this, [tag]); 
29196             return;
29197         }
29198         
29199         
29200         
29201         
29202         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
29203         this.getEditor().Focus();
29204         if (tgs.length) {
29205             if (!this.getEditor().Selection.GetSelection()) {
29206                 this.focus.defer(100,this, [tag]); 
29207                 return;
29208             }
29209             
29210             
29211             var r = this.getEditor().EditorDocument.createRange();
29212             r.setStart(tgs[0],0);
29213             r.setEnd(tgs[0],0);
29214             this.getEditor().Selection.GetSelection().removeAllRanges();
29215             this.getEditor().Selection.GetSelection().addRange(r);
29216             this.getEditor().Focus();
29217         }
29218         
29219     },
29220     
29221     
29222     
29223     replaceTextarea : function()
29224     {
29225         if ( document.getElementById( this.getId() + '___Frame' ) )
29226             return ;
29227         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
29228         //{
29229             // We must check the elements firstly using the Id and then the name.
29230         var oTextarea = document.getElementById( this.getId() );
29231         
29232         var colElementsByName = document.getElementsByName( this.getId() ) ;
29233          
29234         oTextarea.style.display = 'none' ;
29235
29236         if ( oTextarea.tabIndex ) {            
29237             this.TabIndex = oTextarea.tabIndex ;
29238         }
29239         
29240         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
29241         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
29242         this.frame = Roo.get(this.getId() + '___Frame')
29243     },
29244     
29245     _getConfigHtml : function()
29246     {
29247         var sConfig = '' ;
29248
29249         for ( var o in this.fckconfig ) {
29250             sConfig += sConfig.length > 0  ? '&amp;' : '';
29251             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
29252         }
29253
29254         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
29255     },
29256     
29257     
29258     _getIFrameHtml : function()
29259     {
29260         var sFile = 'fckeditor.html' ;
29261         /* no idea what this is about..
29262         try
29263         {
29264             if ( (/fcksource=true/i).test( window.top.location.search ) )
29265                 sFile = 'fckeditor.original.html' ;
29266         }
29267         catch (e) { 
29268         */
29269
29270         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
29271         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
29272         
29273         
29274         var html = '<iframe id="' + this.getId() +
29275             '___Frame" src="' + sLink +
29276             '" width="' + this.width +
29277             '" height="' + this.height + '"' +
29278             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
29279             ' frameborder="0" scrolling="no"></iframe>' ;
29280
29281         return html ;
29282     },
29283     
29284     _insertHtmlBefore : function( html, element )
29285     {
29286         if ( element.insertAdjacentHTML )       {
29287             // IE
29288             element.insertAdjacentHTML( 'beforeBegin', html ) ;
29289         } else { // Gecko
29290             var oRange = document.createRange() ;
29291             oRange.setStartBefore( element ) ;
29292             var oFragment = oRange.createContextualFragment( html );
29293             element.parentNode.insertBefore( oFragment, element ) ;
29294         }
29295     }
29296     
29297     
29298   
29299     
29300     
29301     
29302     
29303
29304 });
29305
29306 //Roo.reg('fckeditor', Roo.form.FCKeditor);
29307
29308 function FCKeditor_OnComplete(editorInstance){
29309     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
29310     f.fckEditor = editorInstance;
29311     //console.log("loaded");
29312     f.fireEvent('editorinit', f, editorInstance);
29313
29314   
29315
29316  
29317
29318
29319
29320
29321
29322
29323
29324
29325
29326
29327
29328
29329
29330
29331
29332 //<script type="text/javascript">
29333 /**
29334  * @class Roo.form.GridField
29335  * @extends Roo.form.Field
29336  * Embed a grid (or editable grid into a form)
29337  * STATUS ALPHA
29338  * 
29339  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
29340  * it needs 
29341  * xgrid.store = Roo.data.Store
29342  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
29343  * xgrid.store.reader = Roo.data.JsonReader 
29344  * 
29345  * 
29346  * @constructor
29347  * Creates a new GridField
29348  * @param {Object} config Configuration options
29349  */
29350 Roo.form.GridField = function(config){
29351     Roo.form.GridField.superclass.constructor.call(this, config);
29352      
29353 };
29354
29355 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
29356     /**
29357      * @cfg {Number} width  - used to restrict width of grid..
29358      */
29359     width : 100,
29360     /**
29361      * @cfg {Number} height - used to restrict height of grid..
29362      */
29363     height : 50,
29364      /**
29365      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
29366          * 
29367          *}
29368      */
29369     xgrid : false, 
29370     /**
29371      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29372      * {tag: "input", type: "checkbox", autocomplete: "off"})
29373      */
29374    // defaultAutoCreate : { tag: 'div' },
29375     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29376     /**
29377      * @cfg {String} addTitle Text to include for adding a title.
29378      */
29379     addTitle : false,
29380     //
29381     onResize : function(){
29382         Roo.form.Field.superclass.onResize.apply(this, arguments);
29383     },
29384
29385     initEvents : function(){
29386         // Roo.form.Checkbox.superclass.initEvents.call(this);
29387         // has no events...
29388        
29389     },
29390
29391
29392     getResizeEl : function(){
29393         return this.wrap;
29394     },
29395
29396     getPositionEl : function(){
29397         return this.wrap;
29398     },
29399
29400     // private
29401     onRender : function(ct, position){
29402         
29403         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
29404         var style = this.style;
29405         delete this.style;
29406         
29407         Roo.form.GridField.superclass.onRender.call(this, ct, position);
29408         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
29409         this.viewEl = this.wrap.createChild({ tag: 'div' });
29410         if (style) {
29411             this.viewEl.applyStyles(style);
29412         }
29413         if (this.width) {
29414             this.viewEl.setWidth(this.width);
29415         }
29416         if (this.height) {
29417             this.viewEl.setHeight(this.height);
29418         }
29419         //if(this.inputValue !== undefined){
29420         //this.setValue(this.value);
29421         
29422         
29423         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
29424         
29425         
29426         this.grid.render();
29427         this.grid.getDataSource().on('remove', this.refreshValue, this);
29428         this.grid.getDataSource().on('update', this.refreshValue, this);
29429         this.grid.on('afteredit', this.refreshValue, this);
29430  
29431     },
29432      
29433     
29434     /**
29435      * Sets the value of the item. 
29436      * @param {String} either an object  or a string..
29437      */
29438     setValue : function(v){
29439         //this.value = v;
29440         v = v || []; // empty set..
29441         // this does not seem smart - it really only affects memoryproxy grids..
29442         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
29443             var ds = this.grid.getDataSource();
29444             // assumes a json reader..
29445             var data = {}
29446             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
29447             ds.loadData( data);
29448         }
29449         // clear selection so it does not get stale.
29450         if (this.grid.sm) { 
29451             this.grid.sm.clearSelections();
29452         }
29453         
29454         Roo.form.GridField.superclass.setValue.call(this, v);
29455         this.refreshValue();
29456         // should load data in the grid really....
29457     },
29458     
29459     // private
29460     refreshValue: function() {
29461          var val = [];
29462         this.grid.getDataSource().each(function(r) {
29463             val.push(r.data);
29464         });
29465         this.el.dom.value = Roo.encode(val);
29466     }
29467     
29468      
29469     
29470     
29471 });/*
29472  * Based on:
29473  * Ext JS Library 1.1.1
29474  * Copyright(c) 2006-2007, Ext JS, LLC.
29475  *
29476  * Originally Released Under LGPL - original licence link has changed is not relivant.
29477  *
29478  * Fork - LGPL
29479  * <script type="text/javascript">
29480  */
29481 /**
29482  * @class Roo.form.DisplayField
29483  * @extends Roo.form.Field
29484  * A generic Field to display non-editable data.
29485  * @constructor
29486  * Creates a new Display Field item.
29487  * @param {Object} config Configuration options
29488  */
29489 Roo.form.DisplayField = function(config){
29490     Roo.form.DisplayField.superclass.constructor.call(this, config);
29491     
29492 };
29493
29494 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
29495     inputType:      'hidden',
29496     allowBlank:     true,
29497     readOnly:         true,
29498     
29499  
29500     /**
29501      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29502      */
29503     focusClass : undefined,
29504     /**
29505      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29506      */
29507     fieldClass: 'x-form-field',
29508     
29509      /**
29510      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
29511      */
29512     valueRenderer: undefined,
29513     
29514     width: 100,
29515     /**
29516      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29517      * {tag: "input", type: "checkbox", autocomplete: "off"})
29518      */
29519      
29520  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29521
29522     onResize : function(){
29523         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
29524         
29525     },
29526
29527     initEvents : function(){
29528         // Roo.form.Checkbox.superclass.initEvents.call(this);
29529         // has no events...
29530        
29531     },
29532
29533
29534     getResizeEl : function(){
29535         return this.wrap;
29536     },
29537
29538     getPositionEl : function(){
29539         return this.wrap;
29540     },
29541
29542     // private
29543     onRender : function(ct, position){
29544         
29545         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
29546         //if(this.inputValue !== undefined){
29547         this.wrap = this.el.wrap();
29548         
29549         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
29550         
29551         if (this.bodyStyle) {
29552             this.viewEl.applyStyles(this.bodyStyle);
29553         }
29554         //this.viewEl.setStyle('padding', '2px');
29555         
29556         this.setValue(this.value);
29557         
29558     },
29559 /*
29560     // private
29561     initValue : Roo.emptyFn,
29562
29563   */
29564
29565         // private
29566     onClick : function(){
29567         
29568     },
29569
29570     /**
29571      * Sets the checked state of the checkbox.
29572      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
29573      */
29574     setValue : function(v){
29575         this.value = v;
29576         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
29577         // this might be called before we have a dom element..
29578         if (!this.viewEl) {
29579             return;
29580         }
29581         this.viewEl.dom.innerHTML = html;
29582         Roo.form.DisplayField.superclass.setValue.call(this, v);
29583
29584     }
29585 });/*
29586  * 
29587  * Licence- LGPL
29588  * 
29589  */
29590
29591 /**
29592  * @class Roo.form.DayPicker
29593  * @extends Roo.form.Field
29594  * A Day picker show [M] [T] [W] ....
29595  * @constructor
29596  * Creates a new Day Picker
29597  * @param {Object} config Configuration options
29598  */
29599 Roo.form.DayPicker= function(config){
29600     Roo.form.DayPicker.superclass.constructor.call(this, config);
29601      
29602 };
29603
29604 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
29605     /**
29606      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29607      */
29608     focusClass : undefined,
29609     /**
29610      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29611      */
29612     fieldClass: "x-form-field",
29613    
29614     /**
29615      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29616      * {tag: "input", type: "checkbox", autocomplete: "off"})
29617      */
29618     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
29619     
29620    
29621     actionMode : 'viewEl', 
29622     //
29623     // private
29624  
29625     inputType : 'hidden',
29626     
29627      
29628     inputElement: false, // real input element?
29629     basedOn: false, // ????
29630     
29631     isFormField: true, // not sure where this is needed!!!!
29632
29633     onResize : function(){
29634         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
29635         if(!this.boxLabel){
29636             this.el.alignTo(this.wrap, 'c-c');
29637         }
29638     },
29639
29640     initEvents : function(){
29641         Roo.form.Checkbox.superclass.initEvents.call(this);
29642         this.el.on("click", this.onClick,  this);
29643         this.el.on("change", this.onClick,  this);
29644     },
29645
29646
29647     getResizeEl : function(){
29648         return this.wrap;
29649     },
29650
29651     getPositionEl : function(){
29652         return this.wrap;
29653     },
29654
29655     
29656     // private
29657     onRender : function(ct, position){
29658         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
29659        
29660         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
29661         
29662         var r1 = '<table><tr>';
29663         var r2 = '<tr class="x-form-daypick-icons">';
29664         for (var i=0; i < 7; i++) {
29665             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
29666             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
29667         }
29668         
29669         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
29670         viewEl.select('img').on('click', this.onClick, this);
29671         this.viewEl = viewEl;   
29672         
29673         
29674         // this will not work on Chrome!!!
29675         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
29676         this.el.on('propertychange', this.setFromHidden,  this);  //ie
29677         
29678         
29679           
29680
29681     },
29682
29683     // private
29684     initValue : Roo.emptyFn,
29685
29686     /**
29687      * Returns the checked state of the checkbox.
29688      * @return {Boolean} True if checked, else false
29689      */
29690     getValue : function(){
29691         return this.el.dom.value;
29692         
29693     },
29694
29695         // private
29696     onClick : function(e){ 
29697         //this.setChecked(!this.checked);
29698         Roo.get(e.target).toggleClass('x-menu-item-checked');
29699         this.refreshValue();
29700         //if(this.el.dom.checked != this.checked){
29701         //    this.setValue(this.el.dom.checked);
29702        // }
29703     },
29704     
29705     // private
29706     refreshValue : function()
29707     {
29708         var val = '';
29709         this.viewEl.select('img',true).each(function(e,i,n)  {
29710             val += e.is(".x-menu-item-checked") ? String(n) : '';
29711         });
29712         this.setValue(val, true);
29713     },
29714
29715     /**
29716      * Sets the checked state of the checkbox.
29717      * On is always based on a string comparison between inputValue and the param.
29718      * @param {Boolean/String} value - the value to set 
29719      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
29720      */
29721     setValue : function(v,suppressEvent){
29722         if (!this.el.dom) {
29723             return;
29724         }
29725         var old = this.el.dom.value ;
29726         this.el.dom.value = v;
29727         if (suppressEvent) {
29728             return ;
29729         }
29730          
29731         // update display..
29732         this.viewEl.select('img',true).each(function(e,i,n)  {
29733             
29734             var on = e.is(".x-menu-item-checked");
29735             var newv = v.indexOf(String(n)) > -1;
29736             if (on != newv) {
29737                 e.toggleClass('x-menu-item-checked');
29738             }
29739             
29740         });
29741         
29742         
29743         this.fireEvent('change', this, v, old);
29744         
29745         
29746     },
29747    
29748     // handle setting of hidden value by some other method!!?!?
29749     setFromHidden: function()
29750     {
29751         if(!this.el){
29752             return;
29753         }
29754         //console.log("SET FROM HIDDEN");
29755         //alert('setFrom hidden');
29756         this.setValue(this.el.dom.value);
29757     },
29758     
29759     onDestroy : function()
29760     {
29761         if(this.viewEl){
29762             Roo.get(this.viewEl).remove();
29763         }
29764          
29765         Roo.form.DayPicker.superclass.onDestroy.call(this);
29766     }
29767
29768 });/*
29769  * RooJS Library 1.1.1
29770  * Copyright(c) 2008-2011  Alan Knowles
29771  *
29772  * License - LGPL
29773  */
29774  
29775
29776 /**
29777  * @class Roo.form.ComboCheck
29778  * @extends Roo.form.ComboBox
29779  * A combobox for multiple select items.
29780  *
29781  * FIXME - could do with a reset button..
29782  * 
29783  * @constructor
29784  * Create a new ComboCheck
29785  * @param {Object} config Configuration options
29786  */
29787 Roo.form.ComboCheck = function(config){
29788     Roo.form.ComboCheck.superclass.constructor.call(this, config);
29789     // should verify some data...
29790     // like
29791     // hiddenName = required..
29792     // displayField = required
29793     // valudField == required
29794     var req= [ 'hiddenName', 'displayField', 'valueField' ];
29795     var _t = this;
29796     Roo.each(req, function(e) {
29797         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
29798             throw "Roo.form.ComboCheck : missing value for: " + e;
29799         }
29800     });
29801     
29802     
29803 };
29804
29805 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
29806      
29807      
29808     editable : false,
29809      
29810     selectedClass: 'x-menu-item-checked', 
29811     
29812     // private
29813     onRender : function(ct, position){
29814         var _t = this;
29815         
29816         
29817         
29818         if(!this.tpl){
29819             var cls = 'x-combo-list';
29820
29821             
29822             this.tpl =  new Roo.Template({
29823                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
29824                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
29825                    '<span>{' + this.displayField + '}</span>' +
29826                     '</div>' 
29827                 
29828             });
29829         }
29830  
29831         
29832         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
29833         this.view.singleSelect = false;
29834         this.view.multiSelect = true;
29835         this.view.toggleSelect = true;
29836         this.pageTb.add(new Roo.Toolbar.Fill(), {
29837             
29838             text: 'Done',
29839             handler: function()
29840             {
29841                 _t.collapse();
29842             }
29843         });
29844     },
29845     
29846     onViewOver : function(e, t){
29847         // do nothing...
29848         return;
29849         
29850     },
29851     
29852     onViewClick : function(doFocus,index){
29853         return;
29854         
29855     },
29856     select: function () {
29857         //Roo.log("SELECT CALLED");
29858     },
29859      
29860     selectByValue : function(xv, scrollIntoView){
29861         var ar = this.getValueArray();
29862         var sels = [];
29863         
29864         Roo.each(ar, function(v) {
29865             if(v === undefined || v === null){
29866                 return;
29867             }
29868             var r = this.findRecord(this.valueField, v);
29869             if(r){
29870                 sels.push(this.store.indexOf(r))
29871                 
29872             }
29873         },this);
29874         this.view.select(sels);
29875         return false;
29876     },
29877     
29878     
29879     
29880     onSelect : function(record, index){
29881        // Roo.log("onselect Called");
29882        // this is only called by the clear button now..
29883         this.view.clearSelections();
29884         this.setValue('[]');
29885         if (this.value != this.valueBefore) {
29886             this.fireEvent('change', this, this.value, this.valueBefore);
29887         }
29888     },
29889     getValueArray : function()
29890     {
29891         var ar = [] ;
29892         
29893         try {
29894             //Roo.log(this.value);
29895             if (typeof(this.value) == 'undefined') {
29896                 return [];
29897             }
29898             var ar = Roo.decode(this.value);
29899             return  ar instanceof Array ? ar : []; //?? valid?
29900             
29901         } catch(e) {
29902             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
29903             return [];
29904         }
29905          
29906     },
29907     expand : function ()
29908     {
29909         Roo.form.ComboCheck.superclass.expand.call(this);
29910         this.valueBefore = this.value;
29911         
29912
29913     },
29914     
29915     collapse : function(){
29916         Roo.form.ComboCheck.superclass.collapse.call(this);
29917         var sl = this.view.getSelectedIndexes();
29918         var st = this.store;
29919         var nv = [];
29920         var tv = [];
29921         var r;
29922         Roo.each(sl, function(i) {
29923             r = st.getAt(i);
29924             nv.push(r.get(this.valueField));
29925         },this);
29926         this.setValue(Roo.encode(nv));
29927         if (this.value != this.valueBefore) {
29928
29929             this.fireEvent('change', this, this.value, this.valueBefore);
29930         }
29931         
29932     },
29933     
29934     setValue : function(v){
29935         // Roo.log(v);
29936         this.value = v;
29937         
29938         var vals = this.getValueArray();
29939         var tv = [];
29940         Roo.each(vals, function(k) {
29941             var r = this.findRecord(this.valueField, k);
29942             if(r){
29943                 tv.push(r.data[this.displayField]);
29944             }else if(this.valueNotFoundText !== undefined){
29945                 tv.push( this.valueNotFoundText );
29946             }
29947         },this);
29948        // Roo.log(tv);
29949         
29950         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
29951         this.hiddenField.value = v;
29952         this.value = v;
29953     }
29954     
29955 });//<script type="text/javasscript">
29956  
29957
29958 /**
29959  * @class Roo.DDView
29960  * A DnD enabled version of Roo.View.
29961  * @param {Element/String} container The Element in which to create the View.
29962  * @param {String} tpl The template string used to create the markup for each element of the View
29963  * @param {Object} config The configuration properties. These include all the config options of
29964  * {@link Roo.View} plus some specific to this class.<br>
29965  * <p>
29966  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
29967  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
29968  * <p>
29969  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
29970 .x-view-drag-insert-above {
29971         border-top:1px dotted #3366cc;
29972 }
29973 .x-view-drag-insert-below {
29974         border-bottom:1px dotted #3366cc;
29975 }
29976 </code></pre>
29977  * 
29978  */
29979  
29980 Roo.DDView = function(container, tpl, config) {
29981     Roo.DDView.superclass.constructor.apply(this, arguments);
29982     this.getEl().setStyle("outline", "0px none");
29983     this.getEl().unselectable();
29984     if (this.dragGroup) {
29985                 this.setDraggable(this.dragGroup.split(","));
29986     }
29987     if (this.dropGroup) {
29988                 this.setDroppable(this.dropGroup.split(","));
29989     }
29990     if (this.deletable) {
29991         this.setDeletable();
29992     }
29993     this.isDirtyFlag = false;
29994         this.addEvents({
29995                 "drop" : true
29996         });
29997 };
29998
29999 Roo.extend(Roo.DDView, Roo.View, {
30000 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
30001 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
30002 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
30003 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
30004
30005         isFormField: true,
30006
30007         reset: Roo.emptyFn,
30008         
30009         clearInvalid: Roo.form.Field.prototype.clearInvalid,
30010
30011         validate: function() {
30012                 return true;
30013         },
30014         
30015         destroy: function() {
30016                 this.purgeListeners();
30017                 this.getEl.removeAllListeners();
30018                 this.getEl().remove();
30019                 if (this.dragZone) {
30020                         if (this.dragZone.destroy) {
30021                                 this.dragZone.destroy();
30022                         }
30023                 }
30024                 if (this.dropZone) {
30025                         if (this.dropZone.destroy) {
30026                                 this.dropZone.destroy();
30027                         }
30028                 }
30029         },
30030
30031 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
30032         getName: function() {
30033                 return this.name;
30034         },
30035
30036 /**     Loads the View from a JSON string representing the Records to put into the Store. */
30037         setValue: function(v) {
30038                 if (!this.store) {
30039                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
30040                 }
30041                 var data = {};
30042                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
30043                 this.store.proxy = new Roo.data.MemoryProxy(data);
30044                 this.store.load();
30045         },
30046
30047 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
30048         getValue: function() {
30049                 var result = '(';
30050                 this.store.each(function(rec) {
30051                         result += rec.id + ',';
30052                 });
30053                 return result.substr(0, result.length - 1) + ')';
30054         },
30055         
30056         getIds: function() {
30057                 var i = 0, result = new Array(this.store.getCount());
30058                 this.store.each(function(rec) {
30059                         result[i++] = rec.id;
30060                 });
30061                 return result;
30062         },
30063         
30064         isDirty: function() {
30065                 return this.isDirtyFlag;
30066         },
30067
30068 /**
30069  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
30070  *      whole Element becomes the target, and this causes the drop gesture to append.
30071  */
30072     getTargetFromEvent : function(e) {
30073                 var target = e.getTarget();
30074                 while ((target !== null) && (target.parentNode != this.el.dom)) {
30075                 target = target.parentNode;
30076                 }
30077                 if (!target) {
30078                         target = this.el.dom.lastChild || this.el.dom;
30079                 }
30080                 return target;
30081     },
30082
30083 /**
30084  *      Create the drag data which consists of an object which has the property "ddel" as
30085  *      the drag proxy element. 
30086  */
30087     getDragData : function(e) {
30088         var target = this.findItemFromChild(e.getTarget());
30089                 if(target) {
30090                         this.handleSelection(e);
30091                         var selNodes = this.getSelectedNodes();
30092             var dragData = {
30093                 source: this,
30094                 copy: this.copy || (this.allowCopy && e.ctrlKey),
30095                 nodes: selNodes,
30096                 records: []
30097                         };
30098                         var selectedIndices = this.getSelectedIndexes();
30099                         for (var i = 0; i < selectedIndices.length; i++) {
30100                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
30101                         }
30102                         if (selNodes.length == 1) {
30103                                 dragData.ddel = target.cloneNode(true); // the div element
30104                         } else {
30105                                 var div = document.createElement('div'); // create the multi element drag "ghost"
30106                                 div.className = 'multi-proxy';
30107                                 for (var i = 0, len = selNodes.length; i < len; i++) {
30108                                         div.appendChild(selNodes[i].cloneNode(true));
30109                                 }
30110                                 dragData.ddel = div;
30111                         }
30112             //console.log(dragData)
30113             //console.log(dragData.ddel.innerHTML)
30114                         return dragData;
30115                 }
30116         //console.log('nodragData')
30117                 return false;
30118     },
30119     
30120 /**     Specify to which ddGroup items in this DDView may be dragged. */
30121     setDraggable: function(ddGroup) {
30122         if (ddGroup instanceof Array) {
30123                 Roo.each(ddGroup, this.setDraggable, this);
30124                 return;
30125         }
30126         if (this.dragZone) {
30127                 this.dragZone.addToGroup(ddGroup);
30128         } else {
30129                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
30130                                 containerScroll: true,
30131                                 ddGroup: ddGroup 
30132
30133                         });
30134 //                      Draggability implies selection. DragZone's mousedown selects the element.
30135                         if (!this.multiSelect) { this.singleSelect = true; }
30136
30137 //                      Wire the DragZone's handlers up to methods in *this*
30138                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
30139                 }
30140     },
30141
30142 /**     Specify from which ddGroup this DDView accepts drops. */
30143     setDroppable: function(ddGroup) {
30144         if (ddGroup instanceof Array) {
30145                 Roo.each(ddGroup, this.setDroppable, this);
30146                 return;
30147         }
30148         if (this.dropZone) {
30149                 this.dropZone.addToGroup(ddGroup);
30150         } else {
30151                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
30152                                 containerScroll: true,
30153                                 ddGroup: ddGroup
30154                         });
30155
30156 //                      Wire the DropZone's handlers up to methods in *this*
30157                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
30158                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
30159                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
30160                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
30161                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
30162                 }
30163     },
30164
30165 /**     Decide whether to drop above or below a View node. */
30166     getDropPoint : function(e, n, dd){
30167         if (n == this.el.dom) { return "above"; }
30168                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
30169                 var c = t + (b - t) / 2;
30170                 var y = Roo.lib.Event.getPageY(e);
30171                 if(y <= c) {
30172                         return "above";
30173                 }else{
30174                         return "below";
30175                 }
30176     },
30177
30178     onNodeEnter : function(n, dd, e, data){
30179                 return false;
30180     },
30181     
30182     onNodeOver : function(n, dd, e, data){
30183                 var pt = this.getDropPoint(e, n, dd);
30184                 // set the insert point style on the target node
30185                 var dragElClass = this.dropNotAllowed;
30186                 if (pt) {
30187                         var targetElClass;
30188                         if (pt == "above"){
30189                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
30190                                 targetElClass = "x-view-drag-insert-above";
30191                         } else {
30192                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
30193                                 targetElClass = "x-view-drag-insert-below";
30194                         }
30195                         if (this.lastInsertClass != targetElClass){
30196                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
30197                                 this.lastInsertClass = targetElClass;
30198                         }
30199                 }
30200                 return dragElClass;
30201         },
30202
30203     onNodeOut : function(n, dd, e, data){
30204                 this.removeDropIndicators(n);
30205     },
30206
30207     onNodeDrop : function(n, dd, e, data){
30208         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
30209                 return false;
30210         }
30211         var pt = this.getDropPoint(e, n, dd);
30212                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
30213                 if (pt == "below") { insertAt++; }
30214                 for (var i = 0; i < data.records.length; i++) {
30215                         var r = data.records[i];
30216                         var dup = this.store.getById(r.id);
30217                         if (dup && (dd != this.dragZone)) {
30218                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
30219                         } else {
30220                                 if (data.copy) {
30221                                         this.store.insert(insertAt++, r.copy());
30222                                 } else {
30223                                         data.source.isDirtyFlag = true;
30224                                         r.store.remove(r);
30225                                         this.store.insert(insertAt++, r);
30226                                 }
30227                                 this.isDirtyFlag = true;
30228                         }
30229                 }
30230                 this.dragZone.cachedTarget = null;
30231                 return true;
30232     },
30233
30234     removeDropIndicators : function(n){
30235                 if(n){
30236                         Roo.fly(n).removeClass([
30237                                 "x-view-drag-insert-above",
30238                                 "x-view-drag-insert-below"]);
30239                         this.lastInsertClass = "_noclass";
30240                 }
30241     },
30242
30243 /**
30244  *      Utility method. Add a delete option to the DDView's context menu.
30245  *      @param {String} imageUrl The URL of the "delete" icon image.
30246  */
30247         setDeletable: function(imageUrl) {
30248                 if (!this.singleSelect && !this.multiSelect) {
30249                         this.singleSelect = true;
30250                 }
30251                 var c = this.getContextMenu();
30252                 this.contextMenu.on("itemclick", function(item) {
30253                         switch (item.id) {
30254                                 case "delete":
30255                                         this.remove(this.getSelectedIndexes());
30256                                         break;
30257                         }
30258                 }, this);
30259                 this.contextMenu.add({
30260                         icon: imageUrl,
30261                         id: "delete",
30262                         text: 'Delete'
30263                 });
30264         },
30265         
30266 /**     Return the context menu for this DDView. */
30267         getContextMenu: function() {
30268                 if (!this.contextMenu) {
30269 //                      Create the View's context menu
30270                         this.contextMenu = new Roo.menu.Menu({
30271                                 id: this.id + "-contextmenu"
30272                         });
30273                         this.el.on("contextmenu", this.showContextMenu, this);
30274                 }
30275                 return this.contextMenu;
30276         },
30277         
30278         disableContextMenu: function() {
30279                 if (this.contextMenu) {
30280                         this.el.un("contextmenu", this.showContextMenu, this);
30281                 }
30282         },
30283
30284         showContextMenu: function(e, item) {
30285         item = this.findItemFromChild(e.getTarget());
30286                 if (item) {
30287                         e.stopEvent();
30288                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
30289                         this.contextMenu.showAt(e.getXY());
30290             }
30291     },
30292
30293 /**
30294  *      Remove {@link Roo.data.Record}s at the specified indices.
30295  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
30296  */
30297     remove: function(selectedIndices) {
30298                 selectedIndices = [].concat(selectedIndices);
30299                 for (var i = 0; i < selectedIndices.length; i++) {
30300                         var rec = this.store.getAt(selectedIndices[i]);
30301                         this.store.remove(rec);
30302                 }
30303     },
30304
30305 /**
30306  *      Double click fires the event, but also, if this is draggable, and there is only one other
30307  *      related DropZone, it transfers the selected node.
30308  */
30309     onDblClick : function(e){
30310         var item = this.findItemFromChild(e.getTarget());
30311         if(item){
30312             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
30313                 return false;
30314             }
30315             if (this.dragGroup) {
30316                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
30317                     while (targets.indexOf(this.dropZone) > -1) {
30318                             targets.remove(this.dropZone);
30319                                 }
30320                     if (targets.length == 1) {
30321                                         this.dragZone.cachedTarget = null;
30322                         var el = Roo.get(targets[0].getEl());
30323                         var box = el.getBox(true);
30324                         targets[0].onNodeDrop(el.dom, {
30325                                 target: el.dom,
30326                                 xy: [box.x, box.y + box.height - 1]
30327                         }, null, this.getDragData(e));
30328                     }
30329                 }
30330         }
30331     },
30332     
30333     handleSelection: function(e) {
30334                 this.dragZone.cachedTarget = null;
30335         var item = this.findItemFromChild(e.getTarget());
30336         if (!item) {
30337                 this.clearSelections(true);
30338                 return;
30339         }
30340                 if (item && (this.multiSelect || this.singleSelect)){
30341                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
30342                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
30343                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
30344                                 this.unselect(item);
30345                         } else {
30346                                 this.select(item, this.multiSelect && e.ctrlKey);
30347                                 this.lastSelection = item;
30348                         }
30349                 }
30350     },
30351
30352     onItemClick : function(item, index, e){
30353                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
30354                         return false;
30355                 }
30356                 return true;
30357     },
30358
30359     unselect : function(nodeInfo, suppressEvent){
30360                 var node = this.getNode(nodeInfo);
30361                 if(node && this.isSelected(node)){
30362                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
30363                                 Roo.fly(node).removeClass(this.selectedClass);
30364                                 this.selections.remove(node);
30365                                 if(!suppressEvent){
30366                                         this.fireEvent("selectionchange", this, this.selections);
30367                                 }
30368                         }
30369                 }
30370     }
30371 });
30372 /*
30373  * Based on:
30374  * Ext JS Library 1.1.1
30375  * Copyright(c) 2006-2007, Ext JS, LLC.
30376  *
30377  * Originally Released Under LGPL - original licence link has changed is not relivant.
30378  *
30379  * Fork - LGPL
30380  * <script type="text/javascript">
30381  */
30382  
30383 /**
30384  * @class Roo.LayoutManager
30385  * @extends Roo.util.Observable
30386  * Base class for layout managers.
30387  */
30388 Roo.LayoutManager = function(container, config){
30389     Roo.LayoutManager.superclass.constructor.call(this);
30390     this.el = Roo.get(container);
30391     // ie scrollbar fix
30392     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
30393         document.body.scroll = "no";
30394     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
30395         this.el.position('relative');
30396     }
30397     this.id = this.el.id;
30398     this.el.addClass("x-layout-container");
30399     /** false to disable window resize monitoring @type Boolean */
30400     this.monitorWindowResize = true;
30401     this.regions = {};
30402     this.addEvents({
30403         /**
30404          * @event layout
30405          * Fires when a layout is performed. 
30406          * @param {Roo.LayoutManager} this
30407          */
30408         "layout" : true,
30409         /**
30410          * @event regionresized
30411          * Fires when the user resizes a region. 
30412          * @param {Roo.LayoutRegion} region The resized region
30413          * @param {Number} newSize The new size (width for east/west, height for north/south)
30414          */
30415         "regionresized" : true,
30416         /**
30417          * @event regioncollapsed
30418          * Fires when a region is collapsed. 
30419          * @param {Roo.LayoutRegion} region The collapsed region
30420          */
30421         "regioncollapsed" : true,
30422         /**
30423          * @event regionexpanded
30424          * Fires when a region is expanded.  
30425          * @param {Roo.LayoutRegion} region The expanded region
30426          */
30427         "regionexpanded" : true
30428     });
30429     this.updating = false;
30430     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
30431 };
30432
30433 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
30434     /**
30435      * Returns true if this layout is currently being updated
30436      * @return {Boolean}
30437      */
30438     isUpdating : function(){
30439         return this.updating; 
30440     },
30441     
30442     /**
30443      * Suspend the LayoutManager from doing auto-layouts while
30444      * making multiple add or remove calls
30445      */
30446     beginUpdate : function(){
30447         this.updating = true;    
30448     },
30449     
30450     /**
30451      * Restore auto-layouts and optionally disable the manager from performing a layout
30452      * @param {Boolean} noLayout true to disable a layout update 
30453      */
30454     endUpdate : function(noLayout){
30455         this.updating = false;
30456         if(!noLayout){
30457             this.layout();
30458         }    
30459     },
30460     
30461     layout: function(){
30462         
30463     },
30464     
30465     onRegionResized : function(region, newSize){
30466         this.fireEvent("regionresized", region, newSize);
30467         this.layout();
30468     },
30469     
30470     onRegionCollapsed : function(region){
30471         this.fireEvent("regioncollapsed", region);
30472     },
30473     
30474     onRegionExpanded : function(region){
30475         this.fireEvent("regionexpanded", region);
30476     },
30477         
30478     /**
30479      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
30480      * performs box-model adjustments.
30481      * @return {Object} The size as an object {width: (the width), height: (the height)}
30482      */
30483     getViewSize : function(){
30484         var size;
30485         if(this.el.dom != document.body){
30486             size = this.el.getSize();
30487         }else{
30488             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
30489         }
30490         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
30491         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30492         return size;
30493     },
30494     
30495     /**
30496      * Returns the Element this layout is bound to.
30497      * @return {Roo.Element}
30498      */
30499     getEl : function(){
30500         return this.el;
30501     },
30502     
30503     /**
30504      * Returns the specified region.
30505      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
30506      * @return {Roo.LayoutRegion}
30507      */
30508     getRegion : function(target){
30509         return this.regions[target.toLowerCase()];
30510     },
30511     
30512     onWindowResize : function(){
30513         if(this.monitorWindowResize){
30514             this.layout();
30515         }
30516     }
30517 });/*
30518  * Based on:
30519  * Ext JS Library 1.1.1
30520  * Copyright(c) 2006-2007, Ext JS, LLC.
30521  *
30522  * Originally Released Under LGPL - original licence link has changed is not relivant.
30523  *
30524  * Fork - LGPL
30525  * <script type="text/javascript">
30526  */
30527 /**
30528  * @class Roo.BorderLayout
30529  * @extends Roo.LayoutManager
30530  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
30531  * please see: <br><br>
30532  * <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>
30533  * <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>
30534  * Example:
30535  <pre><code>
30536  var layout = new Roo.BorderLayout(document.body, {
30537     north: {
30538         initialSize: 25,
30539         titlebar: false
30540     },
30541     west: {
30542         split:true,
30543         initialSize: 200,
30544         minSize: 175,
30545         maxSize: 400,
30546         titlebar: true,
30547         collapsible: true
30548     },
30549     east: {
30550         split:true,
30551         initialSize: 202,
30552         minSize: 175,
30553         maxSize: 400,
30554         titlebar: true,
30555         collapsible: true
30556     },
30557     south: {
30558         split:true,
30559         initialSize: 100,
30560         minSize: 100,
30561         maxSize: 200,
30562         titlebar: true,
30563         collapsible: true
30564     },
30565     center: {
30566         titlebar: true,
30567         autoScroll:true,
30568         resizeTabs: true,
30569         minTabWidth: 50,
30570         preferredTabWidth: 150
30571     }
30572 });
30573
30574 // shorthand
30575 var CP = Roo.ContentPanel;
30576
30577 layout.beginUpdate();
30578 layout.add("north", new CP("north", "North"));
30579 layout.add("south", new CP("south", {title: "South", closable: true}));
30580 layout.add("west", new CP("west", {title: "West"}));
30581 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
30582 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
30583 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
30584 layout.getRegion("center").showPanel("center1");
30585 layout.endUpdate();
30586 </code></pre>
30587
30588 <b>The container the layout is rendered into can be either the body element or any other element.
30589 If it is not the body element, the container needs to either be an absolute positioned element,
30590 or you will need to add "position:relative" to the css of the container.  You will also need to specify
30591 the container size if it is not the body element.</b>
30592
30593 * @constructor
30594 * Create a new BorderLayout
30595 * @param {String/HTMLElement/Element} container The container this layout is bound to
30596 * @param {Object} config Configuration options
30597  */
30598 Roo.BorderLayout = function(container, config){
30599     config = config || {};
30600     Roo.BorderLayout.superclass.constructor.call(this, container, config);
30601     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
30602     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
30603         var target = this.factory.validRegions[i];
30604         if(config[target]){
30605             this.addRegion(target, config[target]);
30606         }
30607     }
30608 };
30609
30610 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
30611     /**
30612      * Creates and adds a new region if it doesn't already exist.
30613      * @param {String} target The target region key (north, south, east, west or center).
30614      * @param {Object} config The regions config object
30615      * @return {BorderLayoutRegion} The new region
30616      */
30617     addRegion : function(target, config){
30618         if(!this.regions[target]){
30619             var r = this.factory.create(target, this, config);
30620             this.bindRegion(target, r);
30621         }
30622         return this.regions[target];
30623     },
30624
30625     // private (kinda)
30626     bindRegion : function(name, r){
30627         this.regions[name] = r;
30628         r.on("visibilitychange", this.layout, this);
30629         r.on("paneladded", this.layout, this);
30630         r.on("panelremoved", this.layout, this);
30631         r.on("invalidated", this.layout, this);
30632         r.on("resized", this.onRegionResized, this);
30633         r.on("collapsed", this.onRegionCollapsed, this);
30634         r.on("expanded", this.onRegionExpanded, this);
30635     },
30636
30637     /**
30638      * Performs a layout update.
30639      */
30640     layout : function(){
30641         if(this.updating) return;
30642         var size = this.getViewSize();
30643         var w = size.width;
30644         var h = size.height;
30645         var centerW = w;
30646         var centerH = h;
30647         var centerY = 0;
30648         var centerX = 0;
30649         //var x = 0, y = 0;
30650
30651         var rs = this.regions;
30652         var north = rs["north"];
30653         var south = rs["south"]; 
30654         var west = rs["west"];
30655         var east = rs["east"];
30656         var center = rs["center"];
30657         //if(this.hideOnLayout){ // not supported anymore
30658             //c.el.setStyle("display", "none");
30659         //}
30660         if(north && north.isVisible()){
30661             var b = north.getBox();
30662             var m = north.getMargins();
30663             b.width = w - (m.left+m.right);
30664             b.x = m.left;
30665             b.y = m.top;
30666             centerY = b.height + b.y + m.bottom;
30667             centerH -= centerY;
30668             north.updateBox(this.safeBox(b));
30669         }
30670         if(south && south.isVisible()){
30671             var b = south.getBox();
30672             var m = south.getMargins();
30673             b.width = w - (m.left+m.right);
30674             b.x = m.left;
30675             var totalHeight = (b.height + m.top + m.bottom);
30676             b.y = h - totalHeight + m.top;
30677             centerH -= totalHeight;
30678             south.updateBox(this.safeBox(b));
30679         }
30680         if(west && west.isVisible()){
30681             var b = west.getBox();
30682             var m = west.getMargins();
30683             b.height = centerH - (m.top+m.bottom);
30684             b.x = m.left;
30685             b.y = centerY + m.top;
30686             var totalWidth = (b.width + m.left + m.right);
30687             centerX += totalWidth;
30688             centerW -= totalWidth;
30689             west.updateBox(this.safeBox(b));
30690         }
30691         if(east && east.isVisible()){
30692             var b = east.getBox();
30693             var m = east.getMargins();
30694             b.height = centerH - (m.top+m.bottom);
30695             var totalWidth = (b.width + m.left + m.right);
30696             b.x = w - totalWidth + m.left;
30697             b.y = centerY + m.top;
30698             centerW -= totalWidth;
30699             east.updateBox(this.safeBox(b));
30700         }
30701         if(center){
30702             var m = center.getMargins();
30703             var centerBox = {
30704                 x: centerX + m.left,
30705                 y: centerY + m.top,
30706                 width: centerW - (m.left+m.right),
30707                 height: centerH - (m.top+m.bottom)
30708             };
30709             //if(this.hideOnLayout){
30710                 //center.el.setStyle("display", "block");
30711             //}
30712             center.updateBox(this.safeBox(centerBox));
30713         }
30714         this.el.repaint();
30715         this.fireEvent("layout", this);
30716     },
30717
30718     // private
30719     safeBox : function(box){
30720         box.width = Math.max(0, box.width);
30721         box.height = Math.max(0, box.height);
30722         return box;
30723     },
30724
30725     /**
30726      * Adds a ContentPanel (or subclass) to this layout.
30727      * @param {String} target The target region key (north, south, east, west or center).
30728      * @param {Roo.ContentPanel} panel The panel to add
30729      * @return {Roo.ContentPanel} The added panel
30730      */
30731     add : function(target, panel){
30732          
30733         target = target.toLowerCase();
30734         return this.regions[target].add(panel);
30735     },
30736
30737     /**
30738      * Remove a ContentPanel (or subclass) to this layout.
30739      * @param {String} target The target region key (north, south, east, west or center).
30740      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
30741      * @return {Roo.ContentPanel} The removed panel
30742      */
30743     remove : function(target, panel){
30744         target = target.toLowerCase();
30745         return this.regions[target].remove(panel);
30746     },
30747
30748     /**
30749      * Searches all regions for a panel with the specified id
30750      * @param {String} panelId
30751      * @return {Roo.ContentPanel} The panel or null if it wasn't found
30752      */
30753     findPanel : function(panelId){
30754         var rs = this.regions;
30755         for(var target in rs){
30756             if(typeof rs[target] != "function"){
30757                 var p = rs[target].getPanel(panelId);
30758                 if(p){
30759                     return p;
30760                 }
30761             }
30762         }
30763         return null;
30764     },
30765
30766     /**
30767      * Searches all regions for a panel with the specified id and activates (shows) it.
30768      * @param {String/ContentPanel} panelId The panels id or the panel itself
30769      * @return {Roo.ContentPanel} The shown panel or null
30770      */
30771     showPanel : function(panelId) {
30772       var rs = this.regions;
30773       for(var target in rs){
30774          var r = rs[target];
30775          if(typeof r != "function"){
30776             if(r.hasPanel(panelId)){
30777                return r.showPanel(panelId);
30778             }
30779          }
30780       }
30781       return null;
30782    },
30783
30784    /**
30785      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
30786      * @param {Roo.state.Provider} provider (optional) An alternate state provider
30787      */
30788     restoreState : function(provider){
30789         if(!provider){
30790             provider = Roo.state.Manager;
30791         }
30792         var sm = new Roo.LayoutStateManager();
30793         sm.init(this, provider);
30794     },
30795
30796     /**
30797      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
30798      * object should contain properties for each region to add ContentPanels to, and each property's value should be
30799      * a valid ContentPanel config object.  Example:
30800      * <pre><code>
30801 // Create the main layout
30802 var layout = new Roo.BorderLayout('main-ct', {
30803     west: {
30804         split:true,
30805         minSize: 175,
30806         titlebar: true
30807     },
30808     center: {
30809         title:'Components'
30810     }
30811 }, 'main-ct');
30812
30813 // Create and add multiple ContentPanels at once via configs
30814 layout.batchAdd({
30815    west: {
30816        id: 'source-files',
30817        autoCreate:true,
30818        title:'Ext Source Files',
30819        autoScroll:true,
30820        fitToFrame:true
30821    },
30822    center : {
30823        el: cview,
30824        autoScroll:true,
30825        fitToFrame:true,
30826        toolbar: tb,
30827        resizeEl:'cbody'
30828    }
30829 });
30830 </code></pre>
30831      * @param {Object} regions An object containing ContentPanel configs by region name
30832      */
30833     batchAdd : function(regions){
30834         this.beginUpdate();
30835         for(var rname in regions){
30836             var lr = this.regions[rname];
30837             if(lr){
30838                 this.addTypedPanels(lr, regions[rname]);
30839             }
30840         }
30841         this.endUpdate();
30842     },
30843
30844     // private
30845     addTypedPanels : function(lr, ps){
30846         if(typeof ps == 'string'){
30847             lr.add(new Roo.ContentPanel(ps));
30848         }
30849         else if(ps instanceof Array){
30850             for(var i =0, len = ps.length; i < len; i++){
30851                 this.addTypedPanels(lr, ps[i]);
30852             }
30853         }
30854         else if(!ps.events){ // raw config?
30855             var el = ps.el;
30856             delete ps.el; // prevent conflict
30857             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
30858         }
30859         else {  // panel object assumed!
30860             lr.add(ps);
30861         }
30862     },
30863     /**
30864      * Adds a xtype elements to the layout.
30865      * <pre><code>
30866
30867 layout.addxtype({
30868        xtype : 'ContentPanel',
30869        region: 'west',
30870        items: [ .... ]
30871    }
30872 );
30873
30874 layout.addxtype({
30875         xtype : 'NestedLayoutPanel',
30876         region: 'west',
30877         layout: {
30878            center: { },
30879            west: { }   
30880         },
30881         items : [ ... list of content panels or nested layout panels.. ]
30882    }
30883 );
30884 </code></pre>
30885      * @param {Object} cfg Xtype definition of item to add.
30886      */
30887     addxtype : function(cfg)
30888     {
30889         // basically accepts a pannel...
30890         // can accept a layout region..!?!?
30891         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
30892         
30893         if (!cfg.xtype.match(/Panel$/)) {
30894             return false;
30895         }
30896         var ret = false;
30897         
30898         if (typeof(cfg.region) == 'undefined') {
30899             Roo.log("Failed to add Panel, region was not set");
30900             Roo.log(cfg);
30901             return false;
30902         }
30903         var region = cfg.region;
30904         delete cfg.region;
30905         
30906           
30907         var xitems = [];
30908         if (cfg.items) {
30909             xitems = cfg.items;
30910             delete cfg.items;
30911         }
30912         var nb = false;
30913         
30914         switch(cfg.xtype) 
30915         {
30916             case 'ContentPanel':  // ContentPanel (el, cfg)
30917             case 'ScrollPanel':  // ContentPanel (el, cfg)
30918                 if(cfg.autoCreate) {
30919                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30920                 } else {
30921                     var el = this.el.createChild();
30922                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
30923                 }
30924                 
30925                 this.add(region, ret);
30926                 break;
30927             
30928             
30929             case 'TreePanel': // our new panel!
30930                 cfg.el = this.el.createChild();
30931                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30932                 this.add(region, ret);
30933                 break;
30934             
30935             case 'NestedLayoutPanel': 
30936                 // create a new Layout (which is  a Border Layout...
30937                 var el = this.el.createChild();
30938                 var clayout = cfg.layout;
30939                 delete cfg.layout;
30940                 clayout.items   = clayout.items  || [];
30941                 // replace this exitems with the clayout ones..
30942                 xitems = clayout.items;
30943                  
30944                 
30945                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
30946                     cfg.background = false;
30947                 }
30948                 var layout = new Roo.BorderLayout(el, clayout);
30949                 
30950                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
30951                 //console.log('adding nested layout panel '  + cfg.toSource());
30952                 this.add(region, ret);
30953                 nb = {}; /// find first...
30954                 break;
30955                 
30956             case 'GridPanel': 
30957             
30958                 // needs grid and region
30959                 
30960                 //var el = this.getRegion(region).el.createChild();
30961                 var el = this.el.createChild();
30962                 // create the grid first...
30963                 
30964                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
30965                 delete cfg.grid;
30966                 if (region == 'center' && this.active ) {
30967                     cfg.background = false;
30968                 }
30969                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
30970                 
30971                 this.add(region, ret);
30972                 if (cfg.background) {
30973                     ret.on('activate', function(gp) {
30974                         if (!gp.grid.rendered) {
30975                             gp.grid.render();
30976                         }
30977                     });
30978                 } else {
30979                     grid.render();
30980                 }
30981                 break;
30982            
30983                
30984                 
30985                 
30986             default: 
30987                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
30988                 return null;
30989              // GridPanel (grid, cfg)
30990             
30991         }
30992         this.beginUpdate();
30993         // add children..
30994         var region = '';
30995         var abn = {};
30996         Roo.each(xitems, function(i)  {
30997             region = nb && i.region ? i.region : false;
30998             
30999             var add = ret.addxtype(i);
31000            
31001             if (region) {
31002                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31003                 if (!i.background) {
31004                     abn[region] = nb[region] ;
31005                 }
31006             }
31007             
31008         });
31009         this.endUpdate();
31010
31011         // make the last non-background panel active..
31012         //if (nb) { Roo.log(abn); }
31013         if (nb) {
31014             
31015             for(var r in abn) {
31016                 region = this.getRegion(r);
31017                 if (region) {
31018                     // tried using nb[r], but it does not work..
31019                      
31020                     region.showPanel(abn[r]);
31021                    
31022                 }
31023             }
31024         }
31025         return ret;
31026         
31027     }
31028 });
31029
31030 /**
31031  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
31032  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
31033  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
31034  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
31035  * <pre><code>
31036 // shorthand
31037 var CP = Roo.ContentPanel;
31038
31039 var layout = Roo.BorderLayout.create({
31040     north: {
31041         initialSize: 25,
31042         titlebar: false,
31043         panels: [new CP("north", "North")]
31044     },
31045     west: {
31046         split:true,
31047         initialSize: 200,
31048         minSize: 175,
31049         maxSize: 400,
31050         titlebar: true,
31051         collapsible: true,
31052         panels: [new CP("west", {title: "West"})]
31053     },
31054     east: {
31055         split:true,
31056         initialSize: 202,
31057         minSize: 175,
31058         maxSize: 400,
31059         titlebar: true,
31060         collapsible: true,
31061         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
31062     },
31063     south: {
31064         split:true,
31065         initialSize: 100,
31066         minSize: 100,
31067         maxSize: 200,
31068         titlebar: true,
31069         collapsible: true,
31070         panels: [new CP("south", {title: "South", closable: true})]
31071     },
31072     center: {
31073         titlebar: true,
31074         autoScroll:true,
31075         resizeTabs: true,
31076         minTabWidth: 50,
31077         preferredTabWidth: 150,
31078         panels: [
31079             new CP("center1", {title: "Close Me", closable: true}),
31080             new CP("center2", {title: "Center Panel", closable: false})
31081         ]
31082     }
31083 }, document.body);
31084
31085 layout.getRegion("center").showPanel("center1");
31086 </code></pre>
31087  * @param config
31088  * @param targetEl
31089  */
31090 Roo.BorderLayout.create = function(config, targetEl){
31091     var layout = new Roo.BorderLayout(targetEl || document.body, config);
31092     layout.beginUpdate();
31093     var regions = Roo.BorderLayout.RegionFactory.validRegions;
31094     for(var j = 0, jlen = regions.length; j < jlen; j++){
31095         var lr = regions[j];
31096         if(layout.regions[lr] && config[lr].panels){
31097             var r = layout.regions[lr];
31098             var ps = config[lr].panels;
31099             layout.addTypedPanels(r, ps);
31100         }
31101     }
31102     layout.endUpdate();
31103     return layout;
31104 };
31105
31106 // private
31107 Roo.BorderLayout.RegionFactory = {
31108     // private
31109     validRegions : ["north","south","east","west","center"],
31110
31111     // private
31112     create : function(target, mgr, config){
31113         target = target.toLowerCase();
31114         if(config.lightweight || config.basic){
31115             return new Roo.BasicLayoutRegion(mgr, config, target);
31116         }
31117         switch(target){
31118             case "north":
31119                 return new Roo.NorthLayoutRegion(mgr, config);
31120             case "south":
31121                 return new Roo.SouthLayoutRegion(mgr, config);
31122             case "east":
31123                 return new Roo.EastLayoutRegion(mgr, config);
31124             case "west":
31125                 return new Roo.WestLayoutRegion(mgr, config);
31126             case "center":
31127                 return new Roo.CenterLayoutRegion(mgr, config);
31128         }
31129         throw 'Layout region "'+target+'" not supported.';
31130     }
31131 };/*
31132  * Based on:
31133  * Ext JS Library 1.1.1
31134  * Copyright(c) 2006-2007, Ext JS, LLC.
31135  *
31136  * Originally Released Under LGPL - original licence link has changed is not relivant.
31137  *
31138  * Fork - LGPL
31139  * <script type="text/javascript">
31140  */
31141  
31142 /**
31143  * @class Roo.BasicLayoutRegion
31144  * @extends Roo.util.Observable
31145  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31146  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31147  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31148  */
31149 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
31150     this.mgr = mgr;
31151     this.position  = pos;
31152     this.events = {
31153         /**
31154          * @scope Roo.BasicLayoutRegion
31155          */
31156         
31157         /**
31158          * @event beforeremove
31159          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31160          * @param {Roo.LayoutRegion} this
31161          * @param {Roo.ContentPanel} panel The panel
31162          * @param {Object} e The cancel event object
31163          */
31164         "beforeremove" : true,
31165         /**
31166          * @event invalidated
31167          * Fires when the layout for this region is changed.
31168          * @param {Roo.LayoutRegion} this
31169          */
31170         "invalidated" : true,
31171         /**
31172          * @event visibilitychange
31173          * Fires when this region is shown or hidden 
31174          * @param {Roo.LayoutRegion} this
31175          * @param {Boolean} visibility true or false
31176          */
31177         "visibilitychange" : true,
31178         /**
31179          * @event paneladded
31180          * Fires when a panel is added. 
31181          * @param {Roo.LayoutRegion} this
31182          * @param {Roo.ContentPanel} panel The panel
31183          */
31184         "paneladded" : true,
31185         /**
31186          * @event panelremoved
31187          * Fires when a panel is removed. 
31188          * @param {Roo.LayoutRegion} this
31189          * @param {Roo.ContentPanel} panel The panel
31190          */
31191         "panelremoved" : true,
31192         /**
31193          * @event collapsed
31194          * Fires when this region is collapsed.
31195          * @param {Roo.LayoutRegion} this
31196          */
31197         "collapsed" : true,
31198         /**
31199          * @event expanded
31200          * Fires when this region is expanded.
31201          * @param {Roo.LayoutRegion} this
31202          */
31203         "expanded" : true,
31204         /**
31205          * @event slideshow
31206          * Fires when this region is slid into view.
31207          * @param {Roo.LayoutRegion} this
31208          */
31209         "slideshow" : true,
31210         /**
31211          * @event slidehide
31212          * Fires when this region slides out of view. 
31213          * @param {Roo.LayoutRegion} this
31214          */
31215         "slidehide" : true,
31216         /**
31217          * @event panelactivated
31218          * Fires when a panel is activated. 
31219          * @param {Roo.LayoutRegion} this
31220          * @param {Roo.ContentPanel} panel The activated panel
31221          */
31222         "panelactivated" : true,
31223         /**
31224          * @event resized
31225          * Fires when the user resizes this region. 
31226          * @param {Roo.LayoutRegion} this
31227          * @param {Number} newSize The new size (width for east/west, height for north/south)
31228          */
31229         "resized" : true
31230     };
31231     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31232     this.panels = new Roo.util.MixedCollection();
31233     this.panels.getKey = this.getPanelId.createDelegate(this);
31234     this.box = null;
31235     this.activePanel = null;
31236     // ensure listeners are added...
31237     
31238     if (config.listeners || config.events) {
31239         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
31240             listeners : config.listeners || {},
31241             events : config.events || {}
31242         });
31243     }
31244     
31245     if(skipConfig !== true){
31246         this.applyConfig(config);
31247     }
31248 };
31249
31250 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
31251     getPanelId : function(p){
31252         return p.getId();
31253     },
31254     
31255     applyConfig : function(config){
31256         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31257         this.config = config;
31258         
31259     },
31260     
31261     /**
31262      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31263      * the width, for horizontal (north, south) the height.
31264      * @param {Number} newSize The new width or height
31265      */
31266     resizeTo : function(newSize){
31267         var el = this.el ? this.el :
31268                  (this.activePanel ? this.activePanel.getEl() : null);
31269         if(el){
31270             switch(this.position){
31271                 case "east":
31272                 case "west":
31273                     el.setWidth(newSize);
31274                     this.fireEvent("resized", this, newSize);
31275                 break;
31276                 case "north":
31277                 case "south":
31278                     el.setHeight(newSize);
31279                     this.fireEvent("resized", this, newSize);
31280                 break;                
31281             }
31282         }
31283     },
31284     
31285     getBox : function(){
31286         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31287     },
31288     
31289     getMargins : function(){
31290         return this.margins;
31291     },
31292     
31293     updateBox : function(box){
31294         this.box = box;
31295         var el = this.activePanel.getEl();
31296         el.dom.style.left = box.x + "px";
31297         el.dom.style.top = box.y + "px";
31298         this.activePanel.setSize(box.width, box.height);
31299     },
31300     
31301     /**
31302      * Returns the container element for this region.
31303      * @return {Roo.Element}
31304      */
31305     getEl : function(){
31306         return this.activePanel;
31307     },
31308     
31309     /**
31310      * Returns true if this region is currently visible.
31311      * @return {Boolean}
31312      */
31313     isVisible : function(){
31314         return this.activePanel ? true : false;
31315     },
31316     
31317     setActivePanel : function(panel){
31318         panel = this.getPanel(panel);
31319         if(this.activePanel && this.activePanel != panel){
31320             this.activePanel.setActiveState(false);
31321             this.activePanel.getEl().setLeftTop(-10000,-10000);
31322         }
31323         this.activePanel = panel;
31324         panel.setActiveState(true);
31325         if(this.box){
31326             panel.setSize(this.box.width, this.box.height);
31327         }
31328         this.fireEvent("panelactivated", this, panel);
31329         this.fireEvent("invalidated");
31330     },
31331     
31332     /**
31333      * Show the specified panel.
31334      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31335      * @return {Roo.ContentPanel} The shown panel or null
31336      */
31337     showPanel : function(panel){
31338         if(panel = this.getPanel(panel)){
31339             this.setActivePanel(panel);
31340         }
31341         return panel;
31342     },
31343     
31344     /**
31345      * Get the active panel for this region.
31346      * @return {Roo.ContentPanel} The active panel or null
31347      */
31348     getActivePanel : function(){
31349         return this.activePanel;
31350     },
31351     
31352     /**
31353      * Add the passed ContentPanel(s)
31354      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31355      * @return {Roo.ContentPanel} The panel added (if only one was added)
31356      */
31357     add : function(panel){
31358         if(arguments.length > 1){
31359             for(var i = 0, len = arguments.length; i < len; i++) {
31360                 this.add(arguments[i]);
31361             }
31362             return null;
31363         }
31364         if(this.hasPanel(panel)){
31365             this.showPanel(panel);
31366             return panel;
31367         }
31368         var el = panel.getEl();
31369         if(el.dom.parentNode != this.mgr.el.dom){
31370             this.mgr.el.dom.appendChild(el.dom);
31371         }
31372         if(panel.setRegion){
31373             panel.setRegion(this);
31374         }
31375         this.panels.add(panel);
31376         el.setStyle("position", "absolute");
31377         if(!panel.background){
31378             this.setActivePanel(panel);
31379             if(this.config.initialSize && this.panels.getCount()==1){
31380                 this.resizeTo(this.config.initialSize);
31381             }
31382         }
31383         this.fireEvent("paneladded", this, panel);
31384         return panel;
31385     },
31386     
31387     /**
31388      * Returns true if the panel is in this region.
31389      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31390      * @return {Boolean}
31391      */
31392     hasPanel : function(panel){
31393         if(typeof panel == "object"){ // must be panel obj
31394             panel = panel.getId();
31395         }
31396         return this.getPanel(panel) ? true : false;
31397     },
31398     
31399     /**
31400      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31401      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31402      * @param {Boolean} preservePanel Overrides the config preservePanel option
31403      * @return {Roo.ContentPanel} The panel that was removed
31404      */
31405     remove : function(panel, preservePanel){
31406         panel = this.getPanel(panel);
31407         if(!panel){
31408             return null;
31409         }
31410         var e = {};
31411         this.fireEvent("beforeremove", this, panel, e);
31412         if(e.cancel === true){
31413             return null;
31414         }
31415         var panelId = panel.getId();
31416         this.panels.removeKey(panelId);
31417         return panel;
31418     },
31419     
31420     /**
31421      * Returns the panel specified or null if it's not in this region.
31422      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31423      * @return {Roo.ContentPanel}
31424      */
31425     getPanel : function(id){
31426         if(typeof id == "object"){ // must be panel obj
31427             return id;
31428         }
31429         return this.panels.get(id);
31430     },
31431     
31432     /**
31433      * Returns this regions position (north/south/east/west/center).
31434      * @return {String} 
31435      */
31436     getPosition: function(){
31437         return this.position;    
31438     }
31439 });/*
31440  * Based on:
31441  * Ext JS Library 1.1.1
31442  * Copyright(c) 2006-2007, Ext JS, LLC.
31443  *
31444  * Originally Released Under LGPL - original licence link has changed is not relivant.
31445  *
31446  * Fork - LGPL
31447  * <script type="text/javascript">
31448  */
31449  
31450 /**
31451  * @class Roo.LayoutRegion
31452  * @extends Roo.BasicLayoutRegion
31453  * This class represents a region in a layout manager.
31454  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
31455  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
31456  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
31457  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
31458  * @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})
31459  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
31460  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
31461  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
31462  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
31463  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
31464  * @cfg {String}    title           The title for the region (overrides panel titles)
31465  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
31466  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
31467  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
31468  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
31469  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
31470  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
31471  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
31472  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
31473  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
31474  * @cfg {Boolean}   showPin         True to show a pin button
31475  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
31476  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
31477  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
31478  * @cfg {Number}    width           For East/West panels
31479  * @cfg {Number}    height          For North/South panels
31480  * @cfg {Boolean}   split           To show the splitter
31481  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
31482  */
31483 Roo.LayoutRegion = function(mgr, config, pos){
31484     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
31485     var dh = Roo.DomHelper;
31486     /** This region's container element 
31487     * @type Roo.Element */
31488     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
31489     /** This region's title element 
31490     * @type Roo.Element */
31491
31492     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
31493         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
31494         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
31495     ]}, true);
31496     this.titleEl.enableDisplayMode();
31497     /** This region's title text element 
31498     * @type HTMLElement */
31499     this.titleTextEl = this.titleEl.dom.firstChild;
31500     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
31501     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
31502     this.closeBtn.enableDisplayMode();
31503     this.closeBtn.on("click", this.closeClicked, this);
31504     this.closeBtn.hide();
31505
31506     this.createBody(config);
31507     this.visible = true;
31508     this.collapsed = false;
31509
31510     if(config.hideWhenEmpty){
31511         this.hide();
31512         this.on("paneladded", this.validateVisibility, this);
31513         this.on("panelremoved", this.validateVisibility, this);
31514     }
31515     this.applyConfig(config);
31516 };
31517
31518 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
31519
31520     createBody : function(){
31521         /** This region's body element 
31522         * @type Roo.Element */
31523         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
31524     },
31525
31526     applyConfig : function(c){
31527         if(c.collapsible && this.position != "center" && !this.collapsedEl){
31528             var dh = Roo.DomHelper;
31529             if(c.titlebar !== false){
31530                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
31531                 this.collapseBtn.on("click", this.collapse, this);
31532                 this.collapseBtn.enableDisplayMode();
31533
31534                 if(c.showPin === true || this.showPin){
31535                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
31536                     this.stickBtn.enableDisplayMode();
31537                     this.stickBtn.on("click", this.expand, this);
31538                     this.stickBtn.hide();
31539                 }
31540             }
31541             /** This region's collapsed element
31542             * @type Roo.Element */
31543             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
31544                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
31545             ]}, true);
31546             if(c.floatable !== false){
31547                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
31548                this.collapsedEl.on("click", this.collapseClick, this);
31549             }
31550
31551             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
31552                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
31553                    id: "message", unselectable: "on", style:{"float":"left"}});
31554                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
31555              }
31556             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
31557             this.expandBtn.on("click", this.expand, this);
31558         }
31559         if(this.collapseBtn){
31560             this.collapseBtn.setVisible(c.collapsible == true);
31561         }
31562         this.cmargins = c.cmargins || this.cmargins ||
31563                          (this.position == "west" || this.position == "east" ?
31564                              {top: 0, left: 2, right:2, bottom: 0} :
31565                              {top: 2, left: 0, right:0, bottom: 2});
31566         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31567         this.bottomTabs = c.tabPosition != "top";
31568         this.autoScroll = c.autoScroll || false;
31569         if(this.autoScroll){
31570             this.bodyEl.setStyle("overflow", "auto");
31571         }else{
31572             this.bodyEl.setStyle("overflow", "hidden");
31573         }
31574         //if(c.titlebar !== false){
31575             if((!c.titlebar && !c.title) || c.titlebar === false){
31576                 this.titleEl.hide();
31577             }else{
31578                 this.titleEl.show();
31579                 if(c.title){
31580                     this.titleTextEl.innerHTML = c.title;
31581                 }
31582             }
31583         //}
31584         this.duration = c.duration || .30;
31585         this.slideDuration = c.slideDuration || .45;
31586         this.config = c;
31587         if(c.collapsed){
31588             this.collapse(true);
31589         }
31590         if(c.hidden){
31591             this.hide();
31592         }
31593     },
31594     /**
31595      * Returns true if this region is currently visible.
31596      * @return {Boolean}
31597      */
31598     isVisible : function(){
31599         return this.visible;
31600     },
31601
31602     /**
31603      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
31604      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
31605      */
31606     setCollapsedTitle : function(title){
31607         title = title || "&#160;";
31608         if(this.collapsedTitleTextEl){
31609             this.collapsedTitleTextEl.innerHTML = title;
31610         }
31611     },
31612
31613     getBox : function(){
31614         var b;
31615         if(!this.collapsed){
31616             b = this.el.getBox(false, true);
31617         }else{
31618             b = this.collapsedEl.getBox(false, true);
31619         }
31620         return b;
31621     },
31622
31623     getMargins : function(){
31624         return this.collapsed ? this.cmargins : this.margins;
31625     },
31626
31627     highlight : function(){
31628         this.el.addClass("x-layout-panel-dragover");
31629     },
31630
31631     unhighlight : function(){
31632         this.el.removeClass("x-layout-panel-dragover");
31633     },
31634
31635     updateBox : function(box){
31636         this.box = box;
31637         if(!this.collapsed){
31638             this.el.dom.style.left = box.x + "px";
31639             this.el.dom.style.top = box.y + "px";
31640             this.updateBody(box.width, box.height);
31641         }else{
31642             this.collapsedEl.dom.style.left = box.x + "px";
31643             this.collapsedEl.dom.style.top = box.y + "px";
31644             this.collapsedEl.setSize(box.width, box.height);
31645         }
31646         if(this.tabs){
31647             this.tabs.autoSizeTabs();
31648         }
31649     },
31650
31651     updateBody : function(w, h){
31652         if(w !== null){
31653             this.el.setWidth(w);
31654             w -= this.el.getBorderWidth("rl");
31655             if(this.config.adjustments){
31656                 w += this.config.adjustments[0];
31657             }
31658         }
31659         if(h !== null){
31660             this.el.setHeight(h);
31661             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
31662             h -= this.el.getBorderWidth("tb");
31663             if(this.config.adjustments){
31664                 h += this.config.adjustments[1];
31665             }
31666             this.bodyEl.setHeight(h);
31667             if(this.tabs){
31668                 h = this.tabs.syncHeight(h);
31669             }
31670         }
31671         if(this.panelSize){
31672             w = w !== null ? w : this.panelSize.width;
31673             h = h !== null ? h : this.panelSize.height;
31674         }
31675         if(this.activePanel){
31676             var el = this.activePanel.getEl();
31677             w = w !== null ? w : el.getWidth();
31678             h = h !== null ? h : el.getHeight();
31679             this.panelSize = {width: w, height: h};
31680             this.activePanel.setSize(w, h);
31681         }
31682         if(Roo.isIE && this.tabs){
31683             this.tabs.el.repaint();
31684         }
31685     },
31686
31687     /**
31688      * Returns the container element for this region.
31689      * @return {Roo.Element}
31690      */
31691     getEl : function(){
31692         return this.el;
31693     },
31694
31695     /**
31696      * Hides this region.
31697      */
31698     hide : function(){
31699         if(!this.collapsed){
31700             this.el.dom.style.left = "-2000px";
31701             this.el.hide();
31702         }else{
31703             this.collapsedEl.dom.style.left = "-2000px";
31704             this.collapsedEl.hide();
31705         }
31706         this.visible = false;
31707         this.fireEvent("visibilitychange", this, false);
31708     },
31709
31710     /**
31711      * Shows this region if it was previously hidden.
31712      */
31713     show : function(){
31714         if(!this.collapsed){
31715             this.el.show();
31716         }else{
31717             this.collapsedEl.show();
31718         }
31719         this.visible = true;
31720         this.fireEvent("visibilitychange", this, true);
31721     },
31722
31723     closeClicked : function(){
31724         if(this.activePanel){
31725             this.remove(this.activePanel);
31726         }
31727     },
31728
31729     collapseClick : function(e){
31730         if(this.isSlid){
31731            e.stopPropagation();
31732            this.slideIn();
31733         }else{
31734            e.stopPropagation();
31735            this.slideOut();
31736         }
31737     },
31738
31739     /**
31740      * Collapses this region.
31741      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
31742      */
31743     collapse : function(skipAnim){
31744         if(this.collapsed) return;
31745         this.collapsed = true;
31746         if(this.split){
31747             this.split.el.hide();
31748         }
31749         if(this.config.animate && skipAnim !== true){
31750             this.fireEvent("invalidated", this);
31751             this.animateCollapse();
31752         }else{
31753             this.el.setLocation(-20000,-20000);
31754             this.el.hide();
31755             this.collapsedEl.show();
31756             this.fireEvent("collapsed", this);
31757             this.fireEvent("invalidated", this);
31758         }
31759     },
31760
31761     animateCollapse : function(){
31762         // overridden
31763     },
31764
31765     /**
31766      * Expands this region if it was previously collapsed.
31767      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
31768      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
31769      */
31770     expand : function(e, skipAnim){
31771         if(e) e.stopPropagation();
31772         if(!this.collapsed || this.el.hasActiveFx()) return;
31773         if(this.isSlid){
31774             this.afterSlideIn();
31775             skipAnim = true;
31776         }
31777         this.collapsed = false;
31778         if(this.config.animate && skipAnim !== true){
31779             this.animateExpand();
31780         }else{
31781             this.el.show();
31782             if(this.split){
31783                 this.split.el.show();
31784             }
31785             this.collapsedEl.setLocation(-2000,-2000);
31786             this.collapsedEl.hide();
31787             this.fireEvent("invalidated", this);
31788             this.fireEvent("expanded", this);
31789         }
31790     },
31791
31792     animateExpand : function(){
31793         // overridden
31794     },
31795
31796     initTabs : function()
31797     {
31798         this.bodyEl.setStyle("overflow", "hidden");
31799         var ts = new Roo.TabPanel(
31800                 this.bodyEl.dom,
31801                 {
31802                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
31803                     disableTooltips: this.config.disableTabTips,
31804                     toolbar : this.config.toolbar
31805                 }
31806         );
31807         if(this.config.hideTabs){
31808             ts.stripWrap.setDisplayed(false);
31809         }
31810         this.tabs = ts;
31811         ts.resizeTabs = this.config.resizeTabs === true;
31812         ts.minTabWidth = this.config.minTabWidth || 40;
31813         ts.maxTabWidth = this.config.maxTabWidth || 250;
31814         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
31815         ts.monitorResize = false;
31816         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31817         ts.bodyEl.addClass('x-layout-tabs-body');
31818         this.panels.each(this.initPanelAsTab, this);
31819     },
31820
31821     initPanelAsTab : function(panel){
31822         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
31823                     this.config.closeOnTab && panel.isClosable());
31824         if(panel.tabTip !== undefined){
31825             ti.setTooltip(panel.tabTip);
31826         }
31827         ti.on("activate", function(){
31828               this.setActivePanel(panel);
31829         }, this);
31830         if(this.config.closeOnTab){
31831             ti.on("beforeclose", function(t, e){
31832                 e.cancel = true;
31833                 this.remove(panel);
31834             }, this);
31835         }
31836         return ti;
31837     },
31838
31839     updatePanelTitle : function(panel, title){
31840         if(this.activePanel == panel){
31841             this.updateTitle(title);
31842         }
31843         if(this.tabs){
31844             var ti = this.tabs.getTab(panel.getEl().id);
31845             ti.setText(title);
31846             if(panel.tabTip !== undefined){
31847                 ti.setTooltip(panel.tabTip);
31848             }
31849         }
31850     },
31851
31852     updateTitle : function(title){
31853         if(this.titleTextEl && !this.config.title){
31854             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
31855         }
31856     },
31857
31858     setActivePanel : function(panel){
31859         panel = this.getPanel(panel);
31860         if(this.activePanel && this.activePanel != panel){
31861             this.activePanel.setActiveState(false);
31862         }
31863         this.activePanel = panel;
31864         panel.setActiveState(true);
31865         if(this.panelSize){
31866             panel.setSize(this.panelSize.width, this.panelSize.height);
31867         }
31868         if(this.closeBtn){
31869             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
31870         }
31871         this.updateTitle(panel.getTitle());
31872         if(this.tabs){
31873             this.fireEvent("invalidated", this);
31874         }
31875         this.fireEvent("panelactivated", this, panel);
31876     },
31877
31878     /**
31879      * Shows the specified panel.
31880      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
31881      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
31882      */
31883     showPanel : function(panel){
31884         if(panel = this.getPanel(panel)){
31885             if(this.tabs){
31886                 var tab = this.tabs.getTab(panel.getEl().id);
31887                 if(tab.isHidden()){
31888                     this.tabs.unhideTab(tab.id);
31889                 }
31890                 tab.activate();
31891             }else{
31892                 this.setActivePanel(panel);
31893             }
31894         }
31895         return panel;
31896     },
31897
31898     /**
31899      * Get the active panel for this region.
31900      * @return {Roo.ContentPanel} The active panel or null
31901      */
31902     getActivePanel : function(){
31903         return this.activePanel;
31904     },
31905
31906     validateVisibility : function(){
31907         if(this.panels.getCount() < 1){
31908             this.updateTitle("&#160;");
31909             this.closeBtn.hide();
31910             this.hide();
31911         }else{
31912             if(!this.isVisible()){
31913                 this.show();
31914             }
31915         }
31916     },
31917
31918     /**
31919      * Adds the passed ContentPanel(s) to this region.
31920      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31921      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
31922      */
31923     add : function(panel){
31924         if(arguments.length > 1){
31925             for(var i = 0, len = arguments.length; i < len; i++) {
31926                 this.add(arguments[i]);
31927             }
31928             return null;
31929         }
31930         if(this.hasPanel(panel)){
31931             this.showPanel(panel);
31932             return panel;
31933         }
31934         panel.setRegion(this);
31935         this.panels.add(panel);
31936         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
31937             this.bodyEl.dom.appendChild(panel.getEl().dom);
31938             if(panel.background !== true){
31939                 this.setActivePanel(panel);
31940             }
31941             this.fireEvent("paneladded", this, panel);
31942             return panel;
31943         }
31944         if(!this.tabs){
31945             this.initTabs();
31946         }else{
31947             this.initPanelAsTab(panel);
31948         }
31949         if(panel.background !== true){
31950             this.tabs.activate(panel.getEl().id);
31951         }
31952         this.fireEvent("paneladded", this, panel);
31953         return panel;
31954     },
31955
31956     /**
31957      * Hides the tab for the specified panel.
31958      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31959      */
31960     hidePanel : function(panel){
31961         if(this.tabs && (panel = this.getPanel(panel))){
31962             this.tabs.hideTab(panel.getEl().id);
31963         }
31964     },
31965
31966     /**
31967      * Unhides the tab for a previously hidden panel.
31968      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31969      */
31970     unhidePanel : function(panel){
31971         if(this.tabs && (panel = this.getPanel(panel))){
31972             this.tabs.unhideTab(panel.getEl().id);
31973         }
31974     },
31975
31976     clearPanels : function(){
31977         while(this.panels.getCount() > 0){
31978              this.remove(this.panels.first());
31979         }
31980     },
31981
31982     /**
31983      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31984      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31985      * @param {Boolean} preservePanel Overrides the config preservePanel option
31986      * @return {Roo.ContentPanel} The panel that was removed
31987      */
31988     remove : function(panel, preservePanel){
31989         panel = this.getPanel(panel);
31990         if(!panel){
31991             return null;
31992         }
31993         var e = {};
31994         this.fireEvent("beforeremove", this, panel, e);
31995         if(e.cancel === true){
31996             return null;
31997         }
31998         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
31999         var panelId = panel.getId();
32000         this.panels.removeKey(panelId);
32001         if(preservePanel){
32002             document.body.appendChild(panel.getEl().dom);
32003         }
32004         if(this.tabs){
32005             this.tabs.removeTab(panel.getEl().id);
32006         }else if (!preservePanel){
32007             this.bodyEl.dom.removeChild(panel.getEl().dom);
32008         }
32009         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32010             var p = this.panels.first();
32011             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32012             tempEl.appendChild(p.getEl().dom);
32013             this.bodyEl.update("");
32014             this.bodyEl.dom.appendChild(p.getEl().dom);
32015             tempEl = null;
32016             this.updateTitle(p.getTitle());
32017             this.tabs = null;
32018             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32019             this.setActivePanel(p);
32020         }
32021         panel.setRegion(null);
32022         if(this.activePanel == panel){
32023             this.activePanel = null;
32024         }
32025         if(this.config.autoDestroy !== false && preservePanel !== true){
32026             try{panel.destroy();}catch(e){}
32027         }
32028         this.fireEvent("panelremoved", this, panel);
32029         return panel;
32030     },
32031
32032     /**
32033      * Returns the TabPanel component used by this region
32034      * @return {Roo.TabPanel}
32035      */
32036     getTabs : function(){
32037         return this.tabs;
32038     },
32039
32040     createTool : function(parentEl, className){
32041         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
32042             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
32043         btn.addClassOnOver("x-layout-tools-button-over");
32044         return btn;
32045     }
32046 });/*
32047  * Based on:
32048  * Ext JS Library 1.1.1
32049  * Copyright(c) 2006-2007, Ext JS, LLC.
32050  *
32051  * Originally Released Under LGPL - original licence link has changed is not relivant.
32052  *
32053  * Fork - LGPL
32054  * <script type="text/javascript">
32055  */
32056  
32057
32058
32059 /**
32060  * @class Roo.SplitLayoutRegion
32061  * @extends Roo.LayoutRegion
32062  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32063  */
32064 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
32065     this.cursor = cursor;
32066     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
32067 };
32068
32069 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
32070     splitTip : "Drag to resize.",
32071     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32072     useSplitTips : false,
32073
32074     applyConfig : function(config){
32075         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
32076         if(config.split){
32077             if(!this.split){
32078                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
32079                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
32080                 /** The SplitBar for this region 
32081                 * @type Roo.SplitBar */
32082                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
32083                 this.split.on("moved", this.onSplitMove, this);
32084                 this.split.useShim = config.useShim === true;
32085                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32086                 if(this.useSplitTips){
32087                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32088                 }
32089                 if(config.collapsible){
32090                     this.split.el.on("dblclick", this.collapse,  this);
32091                 }
32092             }
32093             if(typeof config.minSize != "undefined"){
32094                 this.split.minSize = config.minSize;
32095             }
32096             if(typeof config.maxSize != "undefined"){
32097                 this.split.maxSize = config.maxSize;
32098             }
32099             if(config.hideWhenEmpty || config.hidden || config.collapsed){
32100                 this.hideSplitter();
32101             }
32102         }
32103     },
32104
32105     getHMaxSize : function(){
32106          var cmax = this.config.maxSize || 10000;
32107          var center = this.mgr.getRegion("center");
32108          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32109     },
32110
32111     getVMaxSize : function(){
32112          var cmax = this.config.maxSize || 10000;
32113          var center = this.mgr.getRegion("center");
32114          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32115     },
32116
32117     onSplitMove : function(split, newSize){
32118         this.fireEvent("resized", this, newSize);
32119     },
32120     
32121     /** 
32122      * Returns the {@link Roo.SplitBar} for this region.
32123      * @return {Roo.SplitBar}
32124      */
32125     getSplitBar : function(){
32126         return this.split;
32127     },
32128     
32129     hide : function(){
32130         this.hideSplitter();
32131         Roo.SplitLayoutRegion.superclass.hide.call(this);
32132     },
32133
32134     hideSplitter : function(){
32135         if(this.split){
32136             this.split.el.setLocation(-2000,-2000);
32137             this.split.el.hide();
32138         }
32139     },
32140
32141     show : function(){
32142         if(this.split){
32143             this.split.el.show();
32144         }
32145         Roo.SplitLayoutRegion.superclass.show.call(this);
32146     },
32147     
32148     beforeSlide: function(){
32149         if(Roo.isGecko){// firefox overflow auto bug workaround
32150             this.bodyEl.clip();
32151             if(this.tabs) this.tabs.bodyEl.clip();
32152             if(this.activePanel){
32153                 this.activePanel.getEl().clip();
32154                 
32155                 if(this.activePanel.beforeSlide){
32156                     this.activePanel.beforeSlide();
32157                 }
32158             }
32159         }
32160     },
32161     
32162     afterSlide : function(){
32163         if(Roo.isGecko){// firefox overflow auto bug workaround
32164             this.bodyEl.unclip();
32165             if(this.tabs) this.tabs.bodyEl.unclip();
32166             if(this.activePanel){
32167                 this.activePanel.getEl().unclip();
32168                 if(this.activePanel.afterSlide){
32169                     this.activePanel.afterSlide();
32170                 }
32171             }
32172         }
32173     },
32174
32175     initAutoHide : function(){
32176         if(this.autoHide !== false){
32177             if(!this.autoHideHd){
32178                 var st = new Roo.util.DelayedTask(this.slideIn, this);
32179                 this.autoHideHd = {
32180                     "mouseout": function(e){
32181                         if(!e.within(this.el, true)){
32182                             st.delay(500);
32183                         }
32184                     },
32185                     "mouseover" : function(e){
32186                         st.cancel();
32187                     },
32188                     scope : this
32189                 };
32190             }
32191             this.el.on(this.autoHideHd);
32192         }
32193     },
32194
32195     clearAutoHide : function(){
32196         if(this.autoHide !== false){
32197             this.el.un("mouseout", this.autoHideHd.mouseout);
32198             this.el.un("mouseover", this.autoHideHd.mouseover);
32199         }
32200     },
32201
32202     clearMonitor : function(){
32203         Roo.get(document).un("click", this.slideInIf, this);
32204     },
32205
32206     // these names are backwards but not changed for compat
32207     slideOut : function(){
32208         if(this.isSlid || this.el.hasActiveFx()){
32209             return;
32210         }
32211         this.isSlid = true;
32212         if(this.collapseBtn){
32213             this.collapseBtn.hide();
32214         }
32215         this.closeBtnState = this.closeBtn.getStyle('display');
32216         this.closeBtn.hide();
32217         if(this.stickBtn){
32218             this.stickBtn.show();
32219         }
32220         this.el.show();
32221         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32222         this.beforeSlide();
32223         this.el.setStyle("z-index", 10001);
32224         this.el.slideIn(this.getSlideAnchor(), {
32225             callback: function(){
32226                 this.afterSlide();
32227                 this.initAutoHide();
32228                 Roo.get(document).on("click", this.slideInIf, this);
32229                 this.fireEvent("slideshow", this);
32230             },
32231             scope: this,
32232             block: true
32233         });
32234     },
32235
32236     afterSlideIn : function(){
32237         this.clearAutoHide();
32238         this.isSlid = false;
32239         this.clearMonitor();
32240         this.el.setStyle("z-index", "");
32241         if(this.collapseBtn){
32242             this.collapseBtn.show();
32243         }
32244         this.closeBtn.setStyle('display', this.closeBtnState);
32245         if(this.stickBtn){
32246             this.stickBtn.hide();
32247         }
32248         this.fireEvent("slidehide", this);
32249     },
32250
32251     slideIn : function(cb){
32252         if(!this.isSlid || this.el.hasActiveFx()){
32253             Roo.callback(cb);
32254             return;
32255         }
32256         this.isSlid = false;
32257         this.beforeSlide();
32258         this.el.slideOut(this.getSlideAnchor(), {
32259             callback: function(){
32260                 this.el.setLeftTop(-10000, -10000);
32261                 this.afterSlide();
32262                 this.afterSlideIn();
32263                 Roo.callback(cb);
32264             },
32265             scope: this,
32266             block: true
32267         });
32268     },
32269     
32270     slideInIf : function(e){
32271         if(!e.within(this.el)){
32272             this.slideIn();
32273         }
32274     },
32275
32276     animateCollapse : function(){
32277         this.beforeSlide();
32278         this.el.setStyle("z-index", 20000);
32279         var anchor = this.getSlideAnchor();
32280         this.el.slideOut(anchor, {
32281             callback : function(){
32282                 this.el.setStyle("z-index", "");
32283                 this.collapsedEl.slideIn(anchor, {duration:.3});
32284                 this.afterSlide();
32285                 this.el.setLocation(-10000,-10000);
32286                 this.el.hide();
32287                 this.fireEvent("collapsed", this);
32288             },
32289             scope: this,
32290             block: true
32291         });
32292     },
32293
32294     animateExpand : function(){
32295         this.beforeSlide();
32296         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
32297         this.el.setStyle("z-index", 20000);
32298         this.collapsedEl.hide({
32299             duration:.1
32300         });
32301         this.el.slideIn(this.getSlideAnchor(), {
32302             callback : function(){
32303                 this.el.setStyle("z-index", "");
32304                 this.afterSlide();
32305                 if(this.split){
32306                     this.split.el.show();
32307                 }
32308                 this.fireEvent("invalidated", this);
32309                 this.fireEvent("expanded", this);
32310             },
32311             scope: this,
32312             block: true
32313         });
32314     },
32315
32316     anchors : {
32317         "west" : "left",
32318         "east" : "right",
32319         "north" : "top",
32320         "south" : "bottom"
32321     },
32322
32323     sanchors : {
32324         "west" : "l",
32325         "east" : "r",
32326         "north" : "t",
32327         "south" : "b"
32328     },
32329
32330     canchors : {
32331         "west" : "tl-tr",
32332         "east" : "tr-tl",
32333         "north" : "tl-bl",
32334         "south" : "bl-tl"
32335     },
32336
32337     getAnchor : function(){
32338         return this.anchors[this.position];
32339     },
32340
32341     getCollapseAnchor : function(){
32342         return this.canchors[this.position];
32343     },
32344
32345     getSlideAnchor : function(){
32346         return this.sanchors[this.position];
32347     },
32348
32349     getAlignAdj : function(){
32350         var cm = this.cmargins;
32351         switch(this.position){
32352             case "west":
32353                 return [0, 0];
32354             break;
32355             case "east":
32356                 return [0, 0];
32357             break;
32358             case "north":
32359                 return [0, 0];
32360             break;
32361             case "south":
32362                 return [0, 0];
32363             break;
32364         }
32365     },
32366
32367     getExpandAdj : function(){
32368         var c = this.collapsedEl, cm = this.cmargins;
32369         switch(this.position){
32370             case "west":
32371                 return [-(cm.right+c.getWidth()+cm.left), 0];
32372             break;
32373             case "east":
32374                 return [cm.right+c.getWidth()+cm.left, 0];
32375             break;
32376             case "north":
32377                 return [0, -(cm.top+cm.bottom+c.getHeight())];
32378             break;
32379             case "south":
32380                 return [0, cm.top+cm.bottom+c.getHeight()];
32381             break;
32382         }
32383     }
32384 });/*
32385  * Based on:
32386  * Ext JS Library 1.1.1
32387  * Copyright(c) 2006-2007, Ext JS, LLC.
32388  *
32389  * Originally Released Under LGPL - original licence link has changed is not relivant.
32390  *
32391  * Fork - LGPL
32392  * <script type="text/javascript">
32393  */
32394 /*
32395  * These classes are private internal classes
32396  */
32397 Roo.CenterLayoutRegion = function(mgr, config){
32398     Roo.LayoutRegion.call(this, mgr, config, "center");
32399     this.visible = true;
32400     this.minWidth = config.minWidth || 20;
32401     this.minHeight = config.minHeight || 20;
32402 };
32403
32404 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
32405     hide : function(){
32406         // center panel can't be hidden
32407     },
32408     
32409     show : function(){
32410         // center panel can't be hidden
32411     },
32412     
32413     getMinWidth: function(){
32414         return this.minWidth;
32415     },
32416     
32417     getMinHeight: function(){
32418         return this.minHeight;
32419     }
32420 });
32421
32422
32423 Roo.NorthLayoutRegion = function(mgr, config){
32424     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
32425     if(this.split){
32426         this.split.placement = Roo.SplitBar.TOP;
32427         this.split.orientation = Roo.SplitBar.VERTICAL;
32428         this.split.el.addClass("x-layout-split-v");
32429     }
32430     var size = config.initialSize || config.height;
32431     if(typeof size != "undefined"){
32432         this.el.setHeight(size);
32433     }
32434 };
32435 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
32436     orientation: Roo.SplitBar.VERTICAL,
32437     getBox : function(){
32438         if(this.collapsed){
32439             return this.collapsedEl.getBox();
32440         }
32441         var box = this.el.getBox();
32442         if(this.split){
32443             box.height += this.split.el.getHeight();
32444         }
32445         return box;
32446     },
32447     
32448     updateBox : function(box){
32449         if(this.split && !this.collapsed){
32450             box.height -= this.split.el.getHeight();
32451             this.split.el.setLeft(box.x);
32452             this.split.el.setTop(box.y+box.height);
32453             this.split.el.setWidth(box.width);
32454         }
32455         if(this.collapsed){
32456             this.updateBody(box.width, null);
32457         }
32458         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32459     }
32460 });
32461
32462 Roo.SouthLayoutRegion = function(mgr, config){
32463     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
32464     if(this.split){
32465         this.split.placement = Roo.SplitBar.BOTTOM;
32466         this.split.orientation = Roo.SplitBar.VERTICAL;
32467         this.split.el.addClass("x-layout-split-v");
32468     }
32469     var size = config.initialSize || config.height;
32470     if(typeof size != "undefined"){
32471         this.el.setHeight(size);
32472     }
32473 };
32474 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
32475     orientation: Roo.SplitBar.VERTICAL,
32476     getBox : function(){
32477         if(this.collapsed){
32478             return this.collapsedEl.getBox();
32479         }
32480         var box = this.el.getBox();
32481         if(this.split){
32482             var sh = this.split.el.getHeight();
32483             box.height += sh;
32484             box.y -= sh;
32485         }
32486         return box;
32487     },
32488     
32489     updateBox : function(box){
32490         if(this.split && !this.collapsed){
32491             var sh = this.split.el.getHeight();
32492             box.height -= sh;
32493             box.y += sh;
32494             this.split.el.setLeft(box.x);
32495             this.split.el.setTop(box.y-sh);
32496             this.split.el.setWidth(box.width);
32497         }
32498         if(this.collapsed){
32499             this.updateBody(box.width, null);
32500         }
32501         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32502     }
32503 });
32504
32505 Roo.EastLayoutRegion = function(mgr, config){
32506     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
32507     if(this.split){
32508         this.split.placement = Roo.SplitBar.RIGHT;
32509         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32510         this.split.el.addClass("x-layout-split-h");
32511     }
32512     var size = config.initialSize || config.width;
32513     if(typeof size != "undefined"){
32514         this.el.setWidth(size);
32515     }
32516 };
32517 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
32518     orientation: Roo.SplitBar.HORIZONTAL,
32519     getBox : function(){
32520         if(this.collapsed){
32521             return this.collapsedEl.getBox();
32522         }
32523         var box = this.el.getBox();
32524         if(this.split){
32525             var sw = this.split.el.getWidth();
32526             box.width += sw;
32527             box.x -= sw;
32528         }
32529         return box;
32530     },
32531
32532     updateBox : function(box){
32533         if(this.split && !this.collapsed){
32534             var sw = this.split.el.getWidth();
32535             box.width -= sw;
32536             this.split.el.setLeft(box.x);
32537             this.split.el.setTop(box.y);
32538             this.split.el.setHeight(box.height);
32539             box.x += sw;
32540         }
32541         if(this.collapsed){
32542             this.updateBody(null, box.height);
32543         }
32544         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32545     }
32546 });
32547
32548 Roo.WestLayoutRegion = function(mgr, config){
32549     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
32550     if(this.split){
32551         this.split.placement = Roo.SplitBar.LEFT;
32552         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32553         this.split.el.addClass("x-layout-split-h");
32554     }
32555     var size = config.initialSize || config.width;
32556     if(typeof size != "undefined"){
32557         this.el.setWidth(size);
32558     }
32559 };
32560 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
32561     orientation: Roo.SplitBar.HORIZONTAL,
32562     getBox : function(){
32563         if(this.collapsed){
32564             return this.collapsedEl.getBox();
32565         }
32566         var box = this.el.getBox();
32567         if(this.split){
32568             box.width += this.split.el.getWidth();
32569         }
32570         return box;
32571     },
32572     
32573     updateBox : function(box){
32574         if(this.split && !this.collapsed){
32575             var sw = this.split.el.getWidth();
32576             box.width -= sw;
32577             this.split.el.setLeft(box.x+box.width);
32578             this.split.el.setTop(box.y);
32579             this.split.el.setHeight(box.height);
32580         }
32581         if(this.collapsed){
32582             this.updateBody(null, box.height);
32583         }
32584         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32585     }
32586 });
32587 /*
32588  * Based on:
32589  * Ext JS Library 1.1.1
32590  * Copyright(c) 2006-2007, Ext JS, LLC.
32591  *
32592  * Originally Released Under LGPL - original licence link has changed is not relivant.
32593  *
32594  * Fork - LGPL
32595  * <script type="text/javascript">
32596  */
32597  
32598  
32599 /*
32600  * Private internal class for reading and applying state
32601  */
32602 Roo.LayoutStateManager = function(layout){
32603      // default empty state
32604      this.state = {
32605         north: {},
32606         south: {},
32607         east: {},
32608         west: {}       
32609     };
32610 };
32611
32612 Roo.LayoutStateManager.prototype = {
32613     init : function(layout, provider){
32614         this.provider = provider;
32615         var state = provider.get(layout.id+"-layout-state");
32616         if(state){
32617             var wasUpdating = layout.isUpdating();
32618             if(!wasUpdating){
32619                 layout.beginUpdate();
32620             }
32621             for(var key in state){
32622                 if(typeof state[key] != "function"){
32623                     var rstate = state[key];
32624                     var r = layout.getRegion(key);
32625                     if(r && rstate){
32626                         if(rstate.size){
32627                             r.resizeTo(rstate.size);
32628                         }
32629                         if(rstate.collapsed == true){
32630                             r.collapse(true);
32631                         }else{
32632                             r.expand(null, true);
32633                         }
32634                     }
32635                 }
32636             }
32637             if(!wasUpdating){
32638                 layout.endUpdate();
32639             }
32640             this.state = state; 
32641         }
32642         this.layout = layout;
32643         layout.on("regionresized", this.onRegionResized, this);
32644         layout.on("regioncollapsed", this.onRegionCollapsed, this);
32645         layout.on("regionexpanded", this.onRegionExpanded, this);
32646     },
32647     
32648     storeState : function(){
32649         this.provider.set(this.layout.id+"-layout-state", this.state);
32650     },
32651     
32652     onRegionResized : function(region, newSize){
32653         this.state[region.getPosition()].size = newSize;
32654         this.storeState();
32655     },
32656     
32657     onRegionCollapsed : function(region){
32658         this.state[region.getPosition()].collapsed = true;
32659         this.storeState();
32660     },
32661     
32662     onRegionExpanded : function(region){
32663         this.state[region.getPosition()].collapsed = false;
32664         this.storeState();
32665     }
32666 };/*
32667  * Based on:
32668  * Ext JS Library 1.1.1
32669  * Copyright(c) 2006-2007, Ext JS, LLC.
32670  *
32671  * Originally Released Under LGPL - original licence link has changed is not relivant.
32672  *
32673  * Fork - LGPL
32674  * <script type="text/javascript">
32675  */
32676 /**
32677  * @class Roo.ContentPanel
32678  * @extends Roo.util.Observable
32679  * A basic ContentPanel element.
32680  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
32681  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
32682  * @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
32683  * @cfg {Boolean}   closable      True if the panel can be closed/removed
32684  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
32685  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
32686  * @cfg {Toolbar}   toolbar       A toolbar for this panel
32687  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
32688  * @cfg {String} title          The title for this panel
32689  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
32690  * @cfg {String} url            Calls {@link #setUrl} with this value
32691  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
32692  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
32693  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
32694  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
32695
32696  * @constructor
32697  * Create a new ContentPanel.
32698  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
32699  * @param {String/Object} config A string to set only the title or a config object
32700  * @param {String} content (optional) Set the HTML content for this panel
32701  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
32702  */
32703 Roo.ContentPanel = function(el, config, content){
32704     
32705      
32706     /*
32707     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
32708         config = el;
32709         el = Roo.id();
32710     }
32711     if (config && config.parentLayout) { 
32712         el = config.parentLayout.el.createChild(); 
32713     }
32714     */
32715     if(el.autoCreate){ // xtype is available if this is called from factory
32716         config = el;
32717         el = Roo.id();
32718     }
32719     this.el = Roo.get(el);
32720     if(!this.el && config && config.autoCreate){
32721         if(typeof config.autoCreate == "object"){
32722             if(!config.autoCreate.id){
32723                 config.autoCreate.id = config.id||el;
32724             }
32725             this.el = Roo.DomHelper.append(document.body,
32726                         config.autoCreate, true);
32727         }else{
32728             this.el = Roo.DomHelper.append(document.body,
32729                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
32730         }
32731     }
32732     this.closable = false;
32733     this.loaded = false;
32734     this.active = false;
32735     if(typeof config == "string"){
32736         this.title = config;
32737     }else{
32738         Roo.apply(this, config);
32739     }
32740     
32741     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
32742         this.wrapEl = this.el.wrap();
32743         this.toolbar.container = this.el.insertSibling(false, 'before');
32744         this.toolbar = new Roo.Toolbar(this.toolbar);
32745     }
32746     
32747     
32748     
32749     if(this.resizeEl){
32750         this.resizeEl = Roo.get(this.resizeEl, true);
32751     }else{
32752         this.resizeEl = this.el;
32753     }
32754     this.addEvents({
32755         /**
32756          * @event activate
32757          * Fires when this panel is activated. 
32758          * @param {Roo.ContentPanel} this
32759          */
32760         "activate" : true,
32761         /**
32762          * @event deactivate
32763          * Fires when this panel is activated. 
32764          * @param {Roo.ContentPanel} this
32765          */
32766         "deactivate" : true,
32767
32768         /**
32769          * @event resize
32770          * Fires when this panel is resized if fitToFrame is true.
32771          * @param {Roo.ContentPanel} this
32772          * @param {Number} width The width after any component adjustments
32773          * @param {Number} height The height after any component adjustments
32774          */
32775         "resize" : true,
32776         
32777          /**
32778          * @event render
32779          * Fires when this tab is created
32780          * @param {Roo.ContentPanel} this
32781          */
32782         "render" : true
32783         
32784         
32785         
32786     });
32787     if(this.autoScroll){
32788         this.resizeEl.setStyle("overflow", "auto");
32789     } else {
32790         // fix randome scrolling
32791         this.el.on('scroll', function() {
32792             Roo.log('fix random scolling');
32793             this.scrollTo('top',0); 
32794         });
32795     }
32796     content = content || this.content;
32797     if(content){
32798         this.setContent(content);
32799     }
32800     if(config && config.url){
32801         this.setUrl(this.url, this.params, this.loadOnce);
32802     }
32803     
32804     
32805     
32806     Roo.ContentPanel.superclass.constructor.call(this);
32807     
32808     this.fireEvent('render', this);
32809 };
32810
32811 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
32812     tabTip:'',
32813     setRegion : function(region){
32814         this.region = region;
32815         if(region){
32816            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
32817         }else{
32818            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
32819         } 
32820     },
32821     
32822     /**
32823      * Returns the toolbar for this Panel if one was configured. 
32824      * @return {Roo.Toolbar} 
32825      */
32826     getToolbar : function(){
32827         return this.toolbar;
32828     },
32829     
32830     setActiveState : function(active){
32831         this.active = active;
32832         if(!active){
32833             this.fireEvent("deactivate", this);
32834         }else{
32835             this.fireEvent("activate", this);
32836         }
32837     },
32838     /**
32839      * Updates this panel's element
32840      * @param {String} content The new content
32841      * @param {Boolean} loadScripts (optional) true to look for and process scripts
32842     */
32843     setContent : function(content, loadScripts){
32844         this.el.update(content, loadScripts);
32845     },
32846
32847     ignoreResize : function(w, h){
32848         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
32849             return true;
32850         }else{
32851             this.lastSize = {width: w, height: h};
32852             return false;
32853         }
32854     },
32855     /**
32856      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
32857      * @return {Roo.UpdateManager} The UpdateManager
32858      */
32859     getUpdateManager : function(){
32860         return this.el.getUpdateManager();
32861     },
32862      /**
32863      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
32864      * @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:
32865 <pre><code>
32866 panel.load({
32867     url: "your-url.php",
32868     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
32869     callback: yourFunction,
32870     scope: yourObject, //(optional scope)
32871     discardUrl: false,
32872     nocache: false,
32873     text: "Loading...",
32874     timeout: 30,
32875     scripts: false
32876 });
32877 </code></pre>
32878      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
32879      * 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.
32880      * @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}
32881      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
32882      * @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.
32883      * @return {Roo.ContentPanel} this
32884      */
32885     load : function(){
32886         var um = this.el.getUpdateManager();
32887         um.update.apply(um, arguments);
32888         return this;
32889     },
32890
32891
32892     /**
32893      * 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.
32894      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
32895      * @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)
32896      * @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)
32897      * @return {Roo.UpdateManager} The UpdateManager
32898      */
32899     setUrl : function(url, params, loadOnce){
32900         if(this.refreshDelegate){
32901             this.removeListener("activate", this.refreshDelegate);
32902         }
32903         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
32904         this.on("activate", this.refreshDelegate);
32905         return this.el.getUpdateManager();
32906     },
32907     
32908     _handleRefresh : function(url, params, loadOnce){
32909         if(!loadOnce || !this.loaded){
32910             var updater = this.el.getUpdateManager();
32911             updater.update(url, params, this._setLoaded.createDelegate(this));
32912         }
32913     },
32914     
32915     _setLoaded : function(){
32916         this.loaded = true;
32917     }, 
32918     
32919     /**
32920      * Returns this panel's id
32921      * @return {String} 
32922      */
32923     getId : function(){
32924         return this.el.id;
32925     },
32926     
32927     /** 
32928      * Returns this panel's element - used by regiosn to add.
32929      * @return {Roo.Element} 
32930      */
32931     getEl : function(){
32932         return this.wrapEl || this.el;
32933     },
32934     
32935     adjustForComponents : function(width, height){
32936         if(this.resizeEl != this.el){
32937             width -= this.el.getFrameWidth('lr');
32938             height -= this.el.getFrameWidth('tb');
32939         }
32940         if(this.toolbar){
32941             var te = this.toolbar.getEl();
32942             height -= te.getHeight();
32943             te.setWidth(width);
32944         }
32945         if(this.adjustments){
32946             width += this.adjustments[0];
32947             height += this.adjustments[1];
32948         }
32949         return {"width": width, "height": height};
32950     },
32951     
32952     setSize : function(width, height){
32953         if(this.fitToFrame && !this.ignoreResize(width, height)){
32954             if(this.fitContainer && this.resizeEl != this.el){
32955                 this.el.setSize(width, height);
32956             }
32957             var size = this.adjustForComponents(width, height);
32958             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
32959             this.fireEvent('resize', this, size.width, size.height);
32960         }
32961     },
32962     
32963     /**
32964      * Returns this panel's title
32965      * @return {String} 
32966      */
32967     getTitle : function(){
32968         return this.title;
32969     },
32970     
32971     /**
32972      * Set this panel's title
32973      * @param {String} title
32974      */
32975     setTitle : function(title){
32976         this.title = title;
32977         if(this.region){
32978             this.region.updatePanelTitle(this, title);
32979         }
32980     },
32981     
32982     /**
32983      * Returns true is this panel was configured to be closable
32984      * @return {Boolean} 
32985      */
32986     isClosable : function(){
32987         return this.closable;
32988     },
32989     
32990     beforeSlide : function(){
32991         this.el.clip();
32992         this.resizeEl.clip();
32993     },
32994     
32995     afterSlide : function(){
32996         this.el.unclip();
32997         this.resizeEl.unclip();
32998     },
32999     
33000     /**
33001      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
33002      *   Will fail silently if the {@link #setUrl} method has not been called.
33003      *   This does not activate the panel, just updates its content.
33004      */
33005     refresh : function(){
33006         if(this.refreshDelegate){
33007            this.loaded = false;
33008            this.refreshDelegate();
33009         }
33010     },
33011     
33012     /**
33013      * Destroys this panel
33014      */
33015     destroy : function(){
33016         this.el.removeAllListeners();
33017         var tempEl = document.createElement("span");
33018         tempEl.appendChild(this.el.dom);
33019         tempEl.innerHTML = "";
33020         this.el.remove();
33021         this.el = null;
33022     },
33023     
33024     /**
33025      * form - if the content panel contains a form - this is a reference to it.
33026      * @type {Roo.form.Form}
33027      */
33028     form : false,
33029     /**
33030      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33031      *    This contains a reference to it.
33032      * @type {Roo.View}
33033      */
33034     view : false,
33035     
33036       /**
33037      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33038      * <pre><code>
33039
33040 layout.addxtype({
33041        xtype : 'Form',
33042        items: [ .... ]
33043    }
33044 );
33045
33046 </code></pre>
33047      * @param {Object} cfg Xtype definition of item to add.
33048      */
33049     
33050     addxtype : function(cfg) {
33051         // add form..
33052         if (cfg.xtype.match(/^Form$/)) {
33053             var el = this.el.createChild();
33054
33055             this.form = new  Roo.form.Form(cfg);
33056             
33057             
33058             if ( this.form.allItems.length) this.form.render(el.dom);
33059             return this.form;
33060         }
33061         // should only have one of theses..
33062         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33063             // views..
33064             cfg.el = this.el.appendChild(document.createElement("div"));
33065             // factory?
33066             
33067             var ret = new Roo.factory(cfg);
33068             ret.render && ret.render(false, ''); // render blank..
33069             this.view = ret;
33070             return ret;
33071         }
33072         return false;
33073     }
33074 });
33075
33076 /**
33077  * @class Roo.GridPanel
33078  * @extends Roo.ContentPanel
33079  * @constructor
33080  * Create a new GridPanel.
33081  * @param {Roo.grid.Grid} grid The grid for this panel
33082  * @param {String/Object} config A string to set only the panel's title, or a config object
33083  */
33084 Roo.GridPanel = function(grid, config){
33085     
33086   
33087     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33088         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
33089         
33090     this.wrapper.dom.appendChild(grid.getGridEl().dom);
33091     
33092     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
33093     
33094     if(this.toolbar){
33095         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
33096     }
33097     // xtype created footer. - not sure if will work as we normally have to render first..
33098     if (this.footer && !this.footer.el && this.footer.xtype) {
33099         
33100         this.footer.container = this.grid.getView().getFooterPanel(true);
33101         this.footer.dataSource = this.grid.dataSource;
33102         this.footer = Roo.factory(this.footer, Roo);
33103         
33104     }
33105     
33106     grid.monitorWindowResize = false; // turn off autosizing
33107     grid.autoHeight = false;
33108     grid.autoWidth = false;
33109     this.grid = grid;
33110     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
33111 };
33112
33113 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
33114     getId : function(){
33115         return this.grid.id;
33116     },
33117     
33118     /**
33119      * Returns the grid for this panel
33120      * @return {Roo.grid.Grid} 
33121      */
33122     getGrid : function(){
33123         return this.grid;    
33124     },
33125     
33126     setSize : function(width, height){
33127         if(!this.ignoreResize(width, height)){
33128             var grid = this.grid;
33129             var size = this.adjustForComponents(width, height);
33130             grid.getGridEl().setSize(size.width, size.height);
33131             grid.autoSize();
33132         }
33133     },
33134     
33135     beforeSlide : function(){
33136         this.grid.getView().scroller.clip();
33137     },
33138     
33139     afterSlide : function(){
33140         this.grid.getView().scroller.unclip();
33141     },
33142     
33143     destroy : function(){
33144         this.grid.destroy();
33145         delete this.grid;
33146         Roo.GridPanel.superclass.destroy.call(this); 
33147     }
33148 });
33149
33150
33151 /**
33152  * @class Roo.NestedLayoutPanel
33153  * @extends Roo.ContentPanel
33154  * @constructor
33155  * Create a new NestedLayoutPanel.
33156  * 
33157  * 
33158  * @param {Roo.BorderLayout} layout The layout for this panel
33159  * @param {String/Object} config A string to set only the title or a config object
33160  */
33161 Roo.NestedLayoutPanel = function(layout, config)
33162 {
33163     // construct with only one argument..
33164     /* FIXME - implement nicer consturctors
33165     if (layout.layout) {
33166         config = layout;
33167         layout = config.layout;
33168         delete config.layout;
33169     }
33170     if (layout.xtype && !layout.getEl) {
33171         // then layout needs constructing..
33172         layout = Roo.factory(layout, Roo);
33173     }
33174     */
33175     
33176     
33177     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
33178     
33179     layout.monitorWindowResize = false; // turn off autosizing
33180     this.layout = layout;
33181     this.layout.getEl().addClass("x-layout-nested-layout");
33182     
33183     
33184     
33185     
33186 };
33187
33188 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
33189
33190     setSize : function(width, height){
33191         if(!this.ignoreResize(width, height)){
33192             var size = this.adjustForComponents(width, height);
33193             var el = this.layout.getEl();
33194             el.setSize(size.width, size.height);
33195             var touch = el.dom.offsetWidth;
33196             this.layout.layout();
33197             // ie requires a double layout on the first pass
33198             if(Roo.isIE && !this.initialized){
33199                 this.initialized = true;
33200                 this.layout.layout();
33201             }
33202         }
33203     },
33204     
33205     // activate all subpanels if not currently active..
33206     
33207     setActiveState : function(active){
33208         this.active = active;
33209         if(!active){
33210             this.fireEvent("deactivate", this);
33211             return;
33212         }
33213         
33214         this.fireEvent("activate", this);
33215         // not sure if this should happen before or after..
33216         if (!this.layout) {
33217             return; // should not happen..
33218         }
33219         var reg = false;
33220         for (var r in this.layout.regions) {
33221             reg = this.layout.getRegion(r);
33222             if (reg.getActivePanel()) {
33223                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
33224                 reg.setActivePanel(reg.getActivePanel());
33225                 continue;
33226             }
33227             if (!reg.panels.length) {
33228                 continue;
33229             }
33230             reg.showPanel(reg.getPanel(0));
33231         }
33232         
33233         
33234         
33235         
33236     },
33237     
33238     /**
33239      * Returns the nested BorderLayout for this panel
33240      * @return {Roo.BorderLayout} 
33241      */
33242     getLayout : function(){
33243         return this.layout;
33244     },
33245     
33246      /**
33247      * Adds a xtype elements to the layout of the nested panel
33248      * <pre><code>
33249
33250 panel.addxtype({
33251        xtype : 'ContentPanel',
33252        region: 'west',
33253        items: [ .... ]
33254    }
33255 );
33256
33257 panel.addxtype({
33258         xtype : 'NestedLayoutPanel',
33259         region: 'west',
33260         layout: {
33261            center: { },
33262            west: { }   
33263         },
33264         items : [ ... list of content panels or nested layout panels.. ]
33265    }
33266 );
33267 </code></pre>
33268      * @param {Object} cfg Xtype definition of item to add.
33269      */
33270     addxtype : function(cfg) {
33271         return this.layout.addxtype(cfg);
33272     
33273     }
33274 });
33275
33276 Roo.ScrollPanel = function(el, config, content){
33277     config = config || {};
33278     config.fitToFrame = true;
33279     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
33280     
33281     this.el.dom.style.overflow = "hidden";
33282     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
33283     this.el.removeClass("x-layout-inactive-content");
33284     this.el.on("mousewheel", this.onWheel, this);
33285
33286     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
33287     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
33288     up.unselectable(); down.unselectable();
33289     up.on("click", this.scrollUp, this);
33290     down.on("click", this.scrollDown, this);
33291     up.addClassOnOver("x-scroller-btn-over");
33292     down.addClassOnOver("x-scroller-btn-over");
33293     up.addClassOnClick("x-scroller-btn-click");
33294     down.addClassOnClick("x-scroller-btn-click");
33295     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
33296
33297     this.resizeEl = this.el;
33298     this.el = wrap; this.up = up; this.down = down;
33299 };
33300
33301 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
33302     increment : 100,
33303     wheelIncrement : 5,
33304     scrollUp : function(){
33305         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
33306     },
33307
33308     scrollDown : function(){
33309         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
33310     },
33311
33312     afterScroll : function(){
33313         var el = this.resizeEl;
33314         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
33315         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33316         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33317     },
33318
33319     setSize : function(){
33320         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
33321         this.afterScroll();
33322     },
33323
33324     onWheel : function(e){
33325         var d = e.getWheelDelta();
33326         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
33327         this.afterScroll();
33328         e.stopEvent();
33329     },
33330
33331     setContent : function(content, loadScripts){
33332         this.resizeEl.update(content, loadScripts);
33333     }
33334
33335 });
33336
33337
33338
33339
33340
33341
33342
33343
33344
33345 /**
33346  * @class Roo.TreePanel
33347  * @extends Roo.ContentPanel
33348  * @constructor
33349  * Create a new TreePanel. - defaults to fit/scoll contents.
33350  * @param {String/Object} config A string to set only the panel's title, or a config object
33351  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
33352  */
33353 Roo.TreePanel = function(config){
33354     var el = config.el;
33355     var tree = config.tree;
33356     delete config.tree; 
33357     delete config.el; // hopefull!
33358     
33359     // wrapper for IE7 strict & safari scroll issue
33360     
33361     var treeEl = el.createChild();
33362     config.resizeEl = treeEl;
33363     
33364     
33365     
33366     Roo.TreePanel.superclass.constructor.call(this, el, config);
33367  
33368  
33369     this.tree = new Roo.tree.TreePanel(treeEl , tree);
33370     //console.log(tree);
33371     this.on('activate', function()
33372     {
33373         if (this.tree.rendered) {
33374             return;
33375         }
33376         //console.log('render tree');
33377         this.tree.render();
33378     });
33379     
33380     this.on('resize',  function (cp, w, h) {
33381             this.tree.innerCt.setWidth(w);
33382             this.tree.innerCt.setHeight(h);
33383             this.tree.innerCt.setStyle('overflow-y', 'auto');
33384     });
33385
33386         
33387     
33388 };
33389
33390 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
33391     fitToFrame : true,
33392     autoScroll : true
33393 });
33394
33395
33396
33397
33398
33399
33400
33401
33402
33403
33404
33405 /*
33406  * Based on:
33407  * Ext JS Library 1.1.1
33408  * Copyright(c) 2006-2007, Ext JS, LLC.
33409  *
33410  * Originally Released Under LGPL - original licence link has changed is not relivant.
33411  *
33412  * Fork - LGPL
33413  * <script type="text/javascript">
33414  */
33415  
33416
33417 /**
33418  * @class Roo.ReaderLayout
33419  * @extends Roo.BorderLayout
33420  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
33421  * center region containing two nested regions (a top one for a list view and one for item preview below),
33422  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
33423  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
33424  * expedites the setup of the overall layout and regions for this common application style.
33425  * Example:
33426  <pre><code>
33427 var reader = new Roo.ReaderLayout();
33428 var CP = Roo.ContentPanel;  // shortcut for adding
33429
33430 reader.beginUpdate();
33431 reader.add("north", new CP("north", "North"));
33432 reader.add("west", new CP("west", {title: "West"}));
33433 reader.add("east", new CP("east", {title: "East"}));
33434
33435 reader.regions.listView.add(new CP("listView", "List"));
33436 reader.regions.preview.add(new CP("preview", "Preview"));
33437 reader.endUpdate();
33438 </code></pre>
33439 * @constructor
33440 * Create a new ReaderLayout
33441 * @param {Object} config Configuration options
33442 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
33443 * document.body if omitted)
33444 */
33445 Roo.ReaderLayout = function(config, renderTo){
33446     var c = config || {size:{}};
33447     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
33448         north: c.north !== false ? Roo.apply({
33449             split:false,
33450             initialSize: 32,
33451             titlebar: false
33452         }, c.north) : false,
33453         west: c.west !== false ? Roo.apply({
33454             split:true,
33455             initialSize: 200,
33456             minSize: 175,
33457             maxSize: 400,
33458             titlebar: true,
33459             collapsible: true,
33460             animate: true,
33461             margins:{left:5,right:0,bottom:5,top:5},
33462             cmargins:{left:5,right:5,bottom:5,top:5}
33463         }, c.west) : false,
33464         east: c.east !== false ? Roo.apply({
33465             split:true,
33466             initialSize: 200,
33467             minSize: 175,
33468             maxSize: 400,
33469             titlebar: true,
33470             collapsible: true,
33471             animate: true,
33472             margins:{left:0,right:5,bottom:5,top:5},
33473             cmargins:{left:5,right:5,bottom:5,top:5}
33474         }, c.east) : false,
33475         center: Roo.apply({
33476             tabPosition: 'top',
33477             autoScroll:false,
33478             closeOnTab: true,
33479             titlebar:false,
33480             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
33481         }, c.center)
33482     });
33483
33484     this.el.addClass('x-reader');
33485
33486     this.beginUpdate();
33487
33488     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
33489         south: c.preview !== false ? Roo.apply({
33490             split:true,
33491             initialSize: 200,
33492             minSize: 100,
33493             autoScroll:true,
33494             collapsible:true,
33495             titlebar: true,
33496             cmargins:{top:5,left:0, right:0, bottom:0}
33497         }, c.preview) : false,
33498         center: Roo.apply({
33499             autoScroll:false,
33500             titlebar:false,
33501             minHeight:200
33502         }, c.listView)
33503     });
33504     this.add('center', new Roo.NestedLayoutPanel(inner,
33505             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
33506
33507     this.endUpdate();
33508
33509     this.regions.preview = inner.getRegion('south');
33510     this.regions.listView = inner.getRegion('center');
33511 };
33512
33513 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
33514  * Based on:
33515  * Ext JS Library 1.1.1
33516  * Copyright(c) 2006-2007, Ext JS, LLC.
33517  *
33518  * Originally Released Under LGPL - original licence link has changed is not relivant.
33519  *
33520  * Fork - LGPL
33521  * <script type="text/javascript">
33522  */
33523  
33524 /**
33525  * @class Roo.grid.Grid
33526  * @extends Roo.util.Observable
33527  * This class represents the primary interface of a component based grid control.
33528  * <br><br>Usage:<pre><code>
33529  var grid = new Roo.grid.Grid("my-container-id", {
33530      ds: myDataStore,
33531      cm: myColModel,
33532      selModel: mySelectionModel,
33533      autoSizeColumns: true,
33534      monitorWindowResize: false,
33535      trackMouseOver: true
33536  });
33537  // set any options
33538  grid.render();
33539  * </code></pre>
33540  * <b>Common Problems:</b><br/>
33541  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
33542  * element will correct this<br/>
33543  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
33544  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
33545  * are unpredictable.<br/>
33546  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
33547  * grid to calculate dimensions/offsets.<br/>
33548   * @constructor
33549  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
33550  * The container MUST have some type of size defined for the grid to fill. The container will be
33551  * automatically set to position relative if it isn't already.
33552  * @param {Object} config A config object that sets properties on this grid.
33553  */
33554 Roo.grid.Grid = function(container, config){
33555         // initialize the container
33556         this.container = Roo.get(container);
33557         this.container.update("");
33558         this.container.setStyle("overflow", "hidden");
33559     this.container.addClass('x-grid-container');
33560
33561     this.id = this.container.id;
33562
33563     Roo.apply(this, config);
33564     // check and correct shorthanded configs
33565     if(this.ds){
33566         this.dataSource = this.ds;
33567         delete this.ds;
33568     }
33569     if(this.cm){
33570         this.colModel = this.cm;
33571         delete this.cm;
33572     }
33573     if(this.sm){
33574         this.selModel = this.sm;
33575         delete this.sm;
33576     }
33577
33578     if (this.selModel) {
33579         this.selModel = Roo.factory(this.selModel, Roo.grid);
33580         this.sm = this.selModel;
33581         this.sm.xmodule = this.xmodule || false;
33582     }
33583     if (typeof(this.colModel.config) == 'undefined') {
33584         this.colModel = new Roo.grid.ColumnModel(this.colModel);
33585         this.cm = this.colModel;
33586         this.cm.xmodule = this.xmodule || false;
33587     }
33588     if (this.dataSource) {
33589         this.dataSource= Roo.factory(this.dataSource, Roo.data);
33590         this.ds = this.dataSource;
33591         this.ds.xmodule = this.xmodule || false;
33592          
33593     }
33594     
33595     
33596     
33597     if(this.width){
33598         this.container.setWidth(this.width);
33599     }
33600
33601     if(this.height){
33602         this.container.setHeight(this.height);
33603     }
33604     /** @private */
33605         this.addEvents({
33606         // raw events
33607         /**
33608          * @event click
33609          * The raw click event for the entire grid.
33610          * @param {Roo.EventObject} e
33611          */
33612         "click" : true,
33613         /**
33614          * @event dblclick
33615          * The raw dblclick event for the entire grid.
33616          * @param {Roo.EventObject} e
33617          */
33618         "dblclick" : true,
33619         /**
33620          * @event contextmenu
33621          * The raw contextmenu event for the entire grid.
33622          * @param {Roo.EventObject} e
33623          */
33624         "contextmenu" : true,
33625         /**
33626          * @event mousedown
33627          * The raw mousedown event for the entire grid.
33628          * @param {Roo.EventObject} e
33629          */
33630         "mousedown" : true,
33631         /**
33632          * @event mouseup
33633          * The raw mouseup event for the entire grid.
33634          * @param {Roo.EventObject} e
33635          */
33636         "mouseup" : true,
33637         /**
33638          * @event mouseover
33639          * The raw mouseover event for the entire grid.
33640          * @param {Roo.EventObject} e
33641          */
33642         "mouseover" : true,
33643         /**
33644          * @event mouseout
33645          * The raw mouseout event for the entire grid.
33646          * @param {Roo.EventObject} e
33647          */
33648         "mouseout" : true,
33649         /**
33650          * @event keypress
33651          * The raw keypress event for the entire grid.
33652          * @param {Roo.EventObject} e
33653          */
33654         "keypress" : true,
33655         /**
33656          * @event keydown
33657          * The raw keydown event for the entire grid.
33658          * @param {Roo.EventObject} e
33659          */
33660         "keydown" : true,
33661
33662         // custom events
33663
33664         /**
33665          * @event cellclick
33666          * Fires when a cell is clicked
33667          * @param {Grid} this
33668          * @param {Number} rowIndex
33669          * @param {Number} columnIndex
33670          * @param {Roo.EventObject} e
33671          */
33672         "cellclick" : true,
33673         /**
33674          * @event celldblclick
33675          * Fires when a cell is double clicked
33676          * @param {Grid} this
33677          * @param {Number} rowIndex
33678          * @param {Number} columnIndex
33679          * @param {Roo.EventObject} e
33680          */
33681         "celldblclick" : true,
33682         /**
33683          * @event rowclick
33684          * Fires when a row is clicked
33685          * @param {Grid} this
33686          * @param {Number} rowIndex
33687          * @param {Roo.EventObject} e
33688          */
33689         "rowclick" : true,
33690         /**
33691          * @event rowdblclick
33692          * Fires when a row is double clicked
33693          * @param {Grid} this
33694          * @param {Number} rowIndex
33695          * @param {Roo.EventObject} e
33696          */
33697         "rowdblclick" : true,
33698         /**
33699          * @event headerclick
33700          * Fires when a header is clicked
33701          * @param {Grid} this
33702          * @param {Number} columnIndex
33703          * @param {Roo.EventObject} e
33704          */
33705         "headerclick" : true,
33706         /**
33707          * @event headerdblclick
33708          * Fires when a header cell is double clicked
33709          * @param {Grid} this
33710          * @param {Number} columnIndex
33711          * @param {Roo.EventObject} e
33712          */
33713         "headerdblclick" : true,
33714         /**
33715          * @event rowcontextmenu
33716          * Fires when a row is right clicked
33717          * @param {Grid} this
33718          * @param {Number} rowIndex
33719          * @param {Roo.EventObject} e
33720          */
33721         "rowcontextmenu" : true,
33722         /**
33723          * @event cellcontextmenu
33724          * Fires when a cell is right clicked
33725          * @param {Grid} this
33726          * @param {Number} rowIndex
33727          * @param {Number} cellIndex
33728          * @param {Roo.EventObject} e
33729          */
33730          "cellcontextmenu" : true,
33731         /**
33732          * @event headercontextmenu
33733          * Fires when a header is right clicked
33734          * @param {Grid} this
33735          * @param {Number} columnIndex
33736          * @param {Roo.EventObject} e
33737          */
33738         "headercontextmenu" : true,
33739         /**
33740          * @event bodyscroll
33741          * Fires when the body element is scrolled
33742          * @param {Number} scrollLeft
33743          * @param {Number} scrollTop
33744          */
33745         "bodyscroll" : true,
33746         /**
33747          * @event columnresize
33748          * Fires when the user resizes a column
33749          * @param {Number} columnIndex
33750          * @param {Number} newSize
33751          */
33752         "columnresize" : true,
33753         /**
33754          * @event columnmove
33755          * Fires when the user moves a column
33756          * @param {Number} oldIndex
33757          * @param {Number} newIndex
33758          */
33759         "columnmove" : true,
33760         /**
33761          * @event startdrag
33762          * Fires when row(s) start being dragged
33763          * @param {Grid} this
33764          * @param {Roo.GridDD} dd The drag drop object
33765          * @param {event} e The raw browser event
33766          */
33767         "startdrag" : true,
33768         /**
33769          * @event enddrag
33770          * Fires when a drag operation is complete
33771          * @param {Grid} this
33772          * @param {Roo.GridDD} dd The drag drop object
33773          * @param {event} e The raw browser event
33774          */
33775         "enddrag" : true,
33776         /**
33777          * @event dragdrop
33778          * Fires when dragged row(s) are dropped on a valid DD target
33779          * @param {Grid} this
33780          * @param {Roo.GridDD} dd The drag drop object
33781          * @param {String} targetId The target drag drop object
33782          * @param {event} e The raw browser event
33783          */
33784         "dragdrop" : true,
33785         /**
33786          * @event dragover
33787          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
33788          * @param {Grid} this
33789          * @param {Roo.GridDD} dd The drag drop object
33790          * @param {String} targetId The target drag drop object
33791          * @param {event} e The raw browser event
33792          */
33793         "dragover" : true,
33794         /**
33795          * @event dragenter
33796          *  Fires when the dragged row(s) first cross another DD target while being dragged
33797          * @param {Grid} this
33798          * @param {Roo.GridDD} dd The drag drop object
33799          * @param {String} targetId The target drag drop object
33800          * @param {event} e The raw browser event
33801          */
33802         "dragenter" : true,
33803         /**
33804          * @event dragout
33805          * Fires when the dragged row(s) leave another DD target while being dragged
33806          * @param {Grid} this
33807          * @param {Roo.GridDD} dd The drag drop object
33808          * @param {String} targetId The target drag drop object
33809          * @param {event} e The raw browser event
33810          */
33811         "dragout" : true,
33812         /**
33813          * @event rowclass
33814          * Fires when a row is rendered, so you can change add a style to it.
33815          * @param {GridView} gridview   The grid view
33816          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
33817          */
33818         'rowclass' : true,
33819
33820         /**
33821          * @event render
33822          * Fires when the grid is rendered
33823          * @param {Grid} grid
33824          */
33825         'render' : true
33826     });
33827
33828     Roo.grid.Grid.superclass.constructor.call(this);
33829 };
33830 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
33831     
33832     /**
33833      * @cfg {String} ddGroup - drag drop group.
33834      */
33835
33836     /**
33837      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
33838      */
33839     minColumnWidth : 25,
33840
33841     /**
33842      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
33843      * <b>on initial render.</b> It is more efficient to explicitly size the columns
33844      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
33845      */
33846     autoSizeColumns : false,
33847
33848     /**
33849      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
33850      */
33851     autoSizeHeaders : true,
33852
33853     /**
33854      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
33855      */
33856     monitorWindowResize : true,
33857
33858     /**
33859      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
33860      * rows measured to get a columns size. Default is 0 (all rows).
33861      */
33862     maxRowsToMeasure : 0,
33863
33864     /**
33865      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
33866      */
33867     trackMouseOver : true,
33868
33869     /**
33870     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
33871     */
33872     
33873     /**
33874     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
33875     */
33876     enableDragDrop : false,
33877     
33878     /**
33879     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
33880     */
33881     enableColumnMove : true,
33882     
33883     /**
33884     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
33885     */
33886     enableColumnHide : true,
33887     
33888     /**
33889     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
33890     */
33891     enableRowHeightSync : false,
33892     
33893     /**
33894     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
33895     */
33896     stripeRows : true,
33897     
33898     /**
33899     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
33900     */
33901     autoHeight : false,
33902
33903     /**
33904      * @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.
33905      */
33906     autoExpandColumn : false,
33907
33908     /**
33909     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
33910     * Default is 50.
33911     */
33912     autoExpandMin : 50,
33913
33914     /**
33915     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
33916     */
33917     autoExpandMax : 1000,
33918
33919     /**
33920     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
33921     */
33922     view : null,
33923
33924     /**
33925     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
33926     */
33927     loadMask : false,
33928     /**
33929     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
33930     */
33931     dropTarget: false,
33932     
33933    
33934     
33935     // private
33936     rendered : false,
33937
33938     /**
33939     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
33940     * of a fixed width. Default is false.
33941     */
33942     /**
33943     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
33944     */
33945     /**
33946      * Called once after all setup has been completed and the grid is ready to be rendered.
33947      * @return {Roo.grid.Grid} this
33948      */
33949     render : function()
33950     {
33951         var c = this.container;
33952         // try to detect autoHeight/width mode
33953         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
33954             this.autoHeight = true;
33955         }
33956         var view = this.getView();
33957         view.init(this);
33958
33959         c.on("click", this.onClick, this);
33960         c.on("dblclick", this.onDblClick, this);
33961         c.on("contextmenu", this.onContextMenu, this);
33962         c.on("keydown", this.onKeyDown, this);
33963
33964         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
33965
33966         this.getSelectionModel().init(this);
33967
33968         view.render();
33969
33970         if(this.loadMask){
33971             this.loadMask = new Roo.LoadMask(this.container,
33972                     Roo.apply({store:this.dataSource}, this.loadMask));
33973         }
33974         
33975         
33976         if (this.toolbar && this.toolbar.xtype) {
33977             this.toolbar.container = this.getView().getHeaderPanel(true);
33978             this.toolbar = new Roo.Toolbar(this.toolbar);
33979         }
33980         if (this.footer && this.footer.xtype) {
33981             this.footer.dataSource = this.getDataSource();
33982             this.footer.container = this.getView().getFooterPanel(true);
33983             this.footer = Roo.factory(this.footer, Roo);
33984         }
33985         if (this.dropTarget && this.dropTarget.xtype) {
33986             delete this.dropTarget.xtype;
33987             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
33988         }
33989         
33990         
33991         this.rendered = true;
33992         this.fireEvent('render', this);
33993         return this;
33994     },
33995
33996         /**
33997          * Reconfigures the grid to use a different Store and Column Model.
33998          * The View will be bound to the new objects and refreshed.
33999          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
34000          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
34001          */
34002     reconfigure : function(dataSource, colModel){
34003         if(this.loadMask){
34004             this.loadMask.destroy();
34005             this.loadMask = new Roo.LoadMask(this.container,
34006                     Roo.apply({store:dataSource}, this.loadMask));
34007         }
34008         this.view.bind(dataSource, colModel);
34009         this.dataSource = dataSource;
34010         this.colModel = colModel;
34011         this.view.refresh(true);
34012     },
34013
34014     // private
34015     onKeyDown : function(e){
34016         this.fireEvent("keydown", e);
34017     },
34018
34019     /**
34020      * Destroy this grid.
34021      * @param {Boolean} removeEl True to remove the element
34022      */
34023     destroy : function(removeEl, keepListeners){
34024         if(this.loadMask){
34025             this.loadMask.destroy();
34026         }
34027         var c = this.container;
34028         c.removeAllListeners();
34029         this.view.destroy();
34030         this.colModel.purgeListeners();
34031         if(!keepListeners){
34032             this.purgeListeners();
34033         }
34034         c.update("");
34035         if(removeEl === true){
34036             c.remove();
34037         }
34038     },
34039
34040     // private
34041     processEvent : function(name, e){
34042         this.fireEvent(name, e);
34043         var t = e.getTarget();
34044         var v = this.view;
34045         var header = v.findHeaderIndex(t);
34046         if(header !== false){
34047             this.fireEvent("header" + name, this, header, e);
34048         }else{
34049             var row = v.findRowIndex(t);
34050             var cell = v.findCellIndex(t);
34051             if(row !== false){
34052                 this.fireEvent("row" + name, this, row, e);
34053                 if(cell !== false){
34054                     this.fireEvent("cell" + name, this, row, cell, e);
34055                 }
34056             }
34057         }
34058     },
34059
34060     // private
34061     onClick : function(e){
34062         this.processEvent("click", e);
34063     },
34064
34065     // private
34066     onContextMenu : function(e, t){
34067         this.processEvent("contextmenu", e);
34068     },
34069
34070     // private
34071     onDblClick : function(e){
34072         this.processEvent("dblclick", e);
34073     },
34074
34075     // private
34076     walkCells : function(row, col, step, fn, scope){
34077         var cm = this.colModel, clen = cm.getColumnCount();
34078         var ds = this.dataSource, rlen = ds.getCount(), first = true;
34079         if(step < 0){
34080             if(col < 0){
34081                 row--;
34082                 first = false;
34083             }
34084             while(row >= 0){
34085                 if(!first){
34086                     col = clen-1;
34087                 }
34088                 first = false;
34089                 while(col >= 0){
34090                     if(fn.call(scope || this, row, col, cm) === true){
34091                         return [row, col];
34092                     }
34093                     col--;
34094                 }
34095                 row--;
34096             }
34097         } else {
34098             if(col >= clen){
34099                 row++;
34100                 first = false;
34101             }
34102             while(row < rlen){
34103                 if(!first){
34104                     col = 0;
34105                 }
34106                 first = false;
34107                 while(col < clen){
34108                     if(fn.call(scope || this, row, col, cm) === true){
34109                         return [row, col];
34110                     }
34111                     col++;
34112                 }
34113                 row++;
34114             }
34115         }
34116         return null;
34117     },
34118
34119     // private
34120     getSelections : function(){
34121         return this.selModel.getSelections();
34122     },
34123
34124     /**
34125      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
34126      * but if manual update is required this method will initiate it.
34127      */
34128     autoSize : function(){
34129         if(this.rendered){
34130             this.view.layout();
34131             if(this.view.adjustForScroll){
34132                 this.view.adjustForScroll();
34133             }
34134         }
34135     },
34136
34137     /**
34138      * Returns the grid's underlying element.
34139      * @return {Element} The element
34140      */
34141     getGridEl : function(){
34142         return this.container;
34143     },
34144
34145     // private for compatibility, overridden by editor grid
34146     stopEditing : function(){},
34147
34148     /**
34149      * Returns the grid's SelectionModel.
34150      * @return {SelectionModel}
34151      */
34152     getSelectionModel : function(){
34153         if(!this.selModel){
34154             this.selModel = new Roo.grid.RowSelectionModel();
34155         }
34156         return this.selModel;
34157     },
34158
34159     /**
34160      * Returns the grid's DataSource.
34161      * @return {DataSource}
34162      */
34163     getDataSource : function(){
34164         return this.dataSource;
34165     },
34166
34167     /**
34168      * Returns the grid's ColumnModel.
34169      * @return {ColumnModel}
34170      */
34171     getColumnModel : function(){
34172         return this.colModel;
34173     },
34174
34175     /**
34176      * Returns the grid's GridView object.
34177      * @return {GridView}
34178      */
34179     getView : function(){
34180         if(!this.view){
34181             this.view = new Roo.grid.GridView(this.viewConfig);
34182         }
34183         return this.view;
34184     },
34185     /**
34186      * Called to get grid's drag proxy text, by default returns this.ddText.
34187      * @return {String}
34188      */
34189     getDragDropText : function(){
34190         var count = this.selModel.getCount();
34191         return String.format(this.ddText, count, count == 1 ? '' : 's');
34192     }
34193 });
34194 /**
34195  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
34196  * %0 is replaced with the number of selected rows.
34197  * @type String
34198  */
34199 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
34200  * Based on:
34201  * Ext JS Library 1.1.1
34202  * Copyright(c) 2006-2007, Ext JS, LLC.
34203  *
34204  * Originally Released Under LGPL - original licence link has changed is not relivant.
34205  *
34206  * Fork - LGPL
34207  * <script type="text/javascript">
34208  */
34209  
34210 Roo.grid.AbstractGridView = function(){
34211         this.grid = null;
34212         
34213         this.events = {
34214             "beforerowremoved" : true,
34215             "beforerowsinserted" : true,
34216             "beforerefresh" : true,
34217             "rowremoved" : true,
34218             "rowsinserted" : true,
34219             "rowupdated" : true,
34220             "refresh" : true
34221         };
34222     Roo.grid.AbstractGridView.superclass.constructor.call(this);
34223 };
34224
34225 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
34226     rowClass : "x-grid-row",
34227     cellClass : "x-grid-cell",
34228     tdClass : "x-grid-td",
34229     hdClass : "x-grid-hd",
34230     splitClass : "x-grid-hd-split",
34231     
34232         init: function(grid){
34233         this.grid = grid;
34234                 var cid = this.grid.getGridEl().id;
34235         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
34236         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
34237         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
34238         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
34239         },
34240         
34241         getColumnRenderers : function(){
34242         var renderers = [];
34243         var cm = this.grid.colModel;
34244         var colCount = cm.getColumnCount();
34245         for(var i = 0; i < colCount; i++){
34246             renderers[i] = cm.getRenderer(i);
34247         }
34248         return renderers;
34249     },
34250     
34251     getColumnIds : function(){
34252         var ids = [];
34253         var cm = this.grid.colModel;
34254         var colCount = cm.getColumnCount();
34255         for(var i = 0; i < colCount; i++){
34256             ids[i] = cm.getColumnId(i);
34257         }
34258         return ids;
34259     },
34260     
34261     getDataIndexes : function(){
34262         if(!this.indexMap){
34263             this.indexMap = this.buildIndexMap();
34264         }
34265         return this.indexMap.colToData;
34266     },
34267     
34268     getColumnIndexByDataIndex : function(dataIndex){
34269         if(!this.indexMap){
34270             this.indexMap = this.buildIndexMap();
34271         }
34272         return this.indexMap.dataToCol[dataIndex];
34273     },
34274     
34275     /**
34276      * Set a css style for a column dynamically. 
34277      * @param {Number} colIndex The index of the column
34278      * @param {String} name The css property name
34279      * @param {String} value The css value
34280      */
34281     setCSSStyle : function(colIndex, name, value){
34282         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
34283         Roo.util.CSS.updateRule(selector, name, value);
34284     },
34285     
34286     generateRules : function(cm){
34287         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
34288         Roo.util.CSS.removeStyleSheet(rulesId);
34289         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34290             var cid = cm.getColumnId(i);
34291             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
34292                          this.tdSelector, cid, " {\n}\n",
34293                          this.hdSelector, cid, " {\n}\n",
34294                          this.splitSelector, cid, " {\n}\n");
34295         }
34296         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34297     }
34298 });/*
34299  * Based on:
34300  * Ext JS Library 1.1.1
34301  * Copyright(c) 2006-2007, Ext JS, LLC.
34302  *
34303  * Originally Released Under LGPL - original licence link has changed is not relivant.
34304  *
34305  * Fork - LGPL
34306  * <script type="text/javascript">
34307  */
34308
34309 // private
34310 // This is a support class used internally by the Grid components
34311 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
34312     this.grid = grid;
34313     this.view = grid.getView();
34314     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34315     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
34316     if(hd2){
34317         this.setHandleElId(Roo.id(hd));
34318         this.setOuterHandleElId(Roo.id(hd2));
34319     }
34320     this.scroll = false;
34321 };
34322 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
34323     maxDragWidth: 120,
34324     getDragData : function(e){
34325         var t = Roo.lib.Event.getTarget(e);
34326         var h = this.view.findHeaderCell(t);
34327         if(h){
34328             return {ddel: h.firstChild, header:h};
34329         }
34330         return false;
34331     },
34332
34333     onInitDrag : function(e){
34334         this.view.headersDisabled = true;
34335         var clone = this.dragData.ddel.cloneNode(true);
34336         clone.id = Roo.id();
34337         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
34338         this.proxy.update(clone);
34339         return true;
34340     },
34341
34342     afterValidDrop : function(){
34343         var v = this.view;
34344         setTimeout(function(){
34345             v.headersDisabled = false;
34346         }, 50);
34347     },
34348
34349     afterInvalidDrop : function(){
34350         var v = this.view;
34351         setTimeout(function(){
34352             v.headersDisabled = false;
34353         }, 50);
34354     }
34355 });
34356 /*
34357  * Based on:
34358  * Ext JS Library 1.1.1
34359  * Copyright(c) 2006-2007, Ext JS, LLC.
34360  *
34361  * Originally Released Under LGPL - original licence link has changed is not relivant.
34362  *
34363  * Fork - LGPL
34364  * <script type="text/javascript">
34365  */
34366 // private
34367 // This is a support class used internally by the Grid components
34368 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
34369     this.grid = grid;
34370     this.view = grid.getView();
34371     // split the proxies so they don't interfere with mouse events
34372     this.proxyTop = Roo.DomHelper.append(document.body, {
34373         cls:"col-move-top", html:"&#160;"
34374     }, true);
34375     this.proxyBottom = Roo.DomHelper.append(document.body, {
34376         cls:"col-move-bottom", html:"&#160;"
34377     }, true);
34378     this.proxyTop.hide = this.proxyBottom.hide = function(){
34379         this.setLeftTop(-100,-100);
34380         this.setStyle("visibility", "hidden");
34381     };
34382     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34383     // temporarily disabled
34384     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
34385     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
34386 };
34387 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
34388     proxyOffsets : [-4, -9],
34389     fly: Roo.Element.fly,
34390
34391     getTargetFromEvent : function(e){
34392         var t = Roo.lib.Event.getTarget(e);
34393         var cindex = this.view.findCellIndex(t);
34394         if(cindex !== false){
34395             return this.view.getHeaderCell(cindex);
34396         }
34397         return null;
34398     },
34399
34400     nextVisible : function(h){
34401         var v = this.view, cm = this.grid.colModel;
34402         h = h.nextSibling;
34403         while(h){
34404             if(!cm.isHidden(v.getCellIndex(h))){
34405                 return h;
34406             }
34407             h = h.nextSibling;
34408         }
34409         return null;
34410     },
34411
34412     prevVisible : function(h){
34413         var v = this.view, cm = this.grid.colModel;
34414         h = h.prevSibling;
34415         while(h){
34416             if(!cm.isHidden(v.getCellIndex(h))){
34417                 return h;
34418             }
34419             h = h.prevSibling;
34420         }
34421         return null;
34422     },
34423
34424     positionIndicator : function(h, n, e){
34425         var x = Roo.lib.Event.getPageX(e);
34426         var r = Roo.lib.Dom.getRegion(n.firstChild);
34427         var px, pt, py = r.top + this.proxyOffsets[1];
34428         if((r.right - x) <= (r.right-r.left)/2){
34429             px = r.right+this.view.borderWidth;
34430             pt = "after";
34431         }else{
34432             px = r.left;
34433             pt = "before";
34434         }
34435         var oldIndex = this.view.getCellIndex(h);
34436         var newIndex = this.view.getCellIndex(n);
34437
34438         if(this.grid.colModel.isFixed(newIndex)){
34439             return false;
34440         }
34441
34442         var locked = this.grid.colModel.isLocked(newIndex);
34443
34444         if(pt == "after"){
34445             newIndex++;
34446         }
34447         if(oldIndex < newIndex){
34448             newIndex--;
34449         }
34450         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
34451             return false;
34452         }
34453         px +=  this.proxyOffsets[0];
34454         this.proxyTop.setLeftTop(px, py);
34455         this.proxyTop.show();
34456         if(!this.bottomOffset){
34457             this.bottomOffset = this.view.mainHd.getHeight();
34458         }
34459         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
34460         this.proxyBottom.show();
34461         return pt;
34462     },
34463
34464     onNodeEnter : function(n, dd, e, data){
34465         if(data.header != n){
34466             this.positionIndicator(data.header, n, e);
34467         }
34468     },
34469
34470     onNodeOver : function(n, dd, e, data){
34471         var result = false;
34472         if(data.header != n){
34473             result = this.positionIndicator(data.header, n, e);
34474         }
34475         if(!result){
34476             this.proxyTop.hide();
34477             this.proxyBottom.hide();
34478         }
34479         return result ? this.dropAllowed : this.dropNotAllowed;
34480     },
34481
34482     onNodeOut : function(n, dd, e, data){
34483         this.proxyTop.hide();
34484         this.proxyBottom.hide();
34485     },
34486
34487     onNodeDrop : function(n, dd, e, data){
34488         var h = data.header;
34489         if(h != n){
34490             var cm = this.grid.colModel;
34491             var x = Roo.lib.Event.getPageX(e);
34492             var r = Roo.lib.Dom.getRegion(n.firstChild);
34493             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
34494             var oldIndex = this.view.getCellIndex(h);
34495             var newIndex = this.view.getCellIndex(n);
34496             var locked = cm.isLocked(newIndex);
34497             if(pt == "after"){
34498                 newIndex++;
34499             }
34500             if(oldIndex < newIndex){
34501                 newIndex--;
34502             }
34503             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
34504                 return false;
34505             }
34506             cm.setLocked(oldIndex, locked, true);
34507             cm.moveColumn(oldIndex, newIndex);
34508             this.grid.fireEvent("columnmove", oldIndex, newIndex);
34509             return true;
34510         }
34511         return false;
34512     }
34513 });
34514 /*
34515  * Based on:
34516  * Ext JS Library 1.1.1
34517  * Copyright(c) 2006-2007, Ext JS, LLC.
34518  *
34519  * Originally Released Under LGPL - original licence link has changed is not relivant.
34520  *
34521  * Fork - LGPL
34522  * <script type="text/javascript">
34523  */
34524   
34525 /**
34526  * @class Roo.grid.GridView
34527  * @extends Roo.util.Observable
34528  *
34529  * @constructor
34530  * @param {Object} config
34531  */
34532 Roo.grid.GridView = function(config){
34533     Roo.grid.GridView.superclass.constructor.call(this);
34534     this.el = null;
34535
34536     Roo.apply(this, config);
34537 };
34538
34539 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
34540
34541     
34542     rowClass : "x-grid-row",
34543
34544     cellClass : "x-grid-col",
34545
34546     tdClass : "x-grid-td",
34547
34548     hdClass : "x-grid-hd",
34549
34550     splitClass : "x-grid-split",
34551
34552     sortClasses : ["sort-asc", "sort-desc"],
34553
34554     enableMoveAnim : false,
34555
34556     hlColor: "C3DAF9",
34557
34558     dh : Roo.DomHelper,
34559
34560     fly : Roo.Element.fly,
34561
34562     css : Roo.util.CSS,
34563
34564     borderWidth: 1,
34565
34566     splitOffset: 3,
34567
34568     scrollIncrement : 22,
34569
34570     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
34571
34572     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
34573
34574     bind : function(ds, cm){
34575         if(this.ds){
34576             this.ds.un("load", this.onLoad, this);
34577             this.ds.un("datachanged", this.onDataChange, this);
34578             this.ds.un("add", this.onAdd, this);
34579             this.ds.un("remove", this.onRemove, this);
34580             this.ds.un("update", this.onUpdate, this);
34581             this.ds.un("clear", this.onClear, this);
34582         }
34583         if(ds){
34584             ds.on("load", this.onLoad, this);
34585             ds.on("datachanged", this.onDataChange, this);
34586             ds.on("add", this.onAdd, this);
34587             ds.on("remove", this.onRemove, this);
34588             ds.on("update", this.onUpdate, this);
34589             ds.on("clear", this.onClear, this);
34590         }
34591         this.ds = ds;
34592
34593         if(this.cm){
34594             this.cm.un("widthchange", this.onColWidthChange, this);
34595             this.cm.un("headerchange", this.onHeaderChange, this);
34596             this.cm.un("hiddenchange", this.onHiddenChange, this);
34597             this.cm.un("columnmoved", this.onColumnMove, this);
34598             this.cm.un("columnlockchange", this.onColumnLock, this);
34599         }
34600         if(cm){
34601             this.generateRules(cm);
34602             cm.on("widthchange", this.onColWidthChange, this);
34603             cm.on("headerchange", this.onHeaderChange, this);
34604             cm.on("hiddenchange", this.onHiddenChange, this);
34605             cm.on("columnmoved", this.onColumnMove, this);
34606             cm.on("columnlockchange", this.onColumnLock, this);
34607         }
34608         this.cm = cm;
34609     },
34610
34611     init: function(grid){
34612         Roo.grid.GridView.superclass.init.call(this, grid);
34613
34614         this.bind(grid.dataSource, grid.colModel);
34615
34616         grid.on("headerclick", this.handleHeaderClick, this);
34617
34618         if(grid.trackMouseOver){
34619             grid.on("mouseover", this.onRowOver, this);
34620             grid.on("mouseout", this.onRowOut, this);
34621         }
34622         grid.cancelTextSelection = function(){};
34623         this.gridId = grid.id;
34624
34625         var tpls = this.templates || {};
34626
34627         if(!tpls.master){
34628             tpls.master = new Roo.Template(
34629                '<div class="x-grid" hidefocus="true">',
34630                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
34631                   '<div class="x-grid-topbar"></div>',
34632                   '<div class="x-grid-scroller"><div></div></div>',
34633                   '<div class="x-grid-locked">',
34634                       '<div class="x-grid-header">{lockedHeader}</div>',
34635                       '<div class="x-grid-body">{lockedBody}</div>',
34636                   "</div>",
34637                   '<div class="x-grid-viewport">',
34638                       '<div class="x-grid-header">{header}</div>',
34639                       '<div class="x-grid-body">{body}</div>',
34640                   "</div>",
34641                   '<div class="x-grid-bottombar"></div>',
34642                  
34643                   '<div class="x-grid-resize-proxy">&#160;</div>',
34644                "</div>"
34645             );
34646             tpls.master.disableformats = true;
34647         }
34648
34649         if(!tpls.header){
34650             tpls.header = new Roo.Template(
34651                '<table border="0" cellspacing="0" cellpadding="0">',
34652                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
34653                "</table>{splits}"
34654             );
34655             tpls.header.disableformats = true;
34656         }
34657         tpls.header.compile();
34658
34659         if(!tpls.hcell){
34660             tpls.hcell = new Roo.Template(
34661                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
34662                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
34663                 "</div></td>"
34664              );
34665              tpls.hcell.disableFormats = true;
34666         }
34667         tpls.hcell.compile();
34668
34669         if(!tpls.hsplit){
34670             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
34671             tpls.hsplit.disableFormats = true;
34672         }
34673         tpls.hsplit.compile();
34674
34675         if(!tpls.body){
34676             tpls.body = new Roo.Template(
34677                '<table border="0" cellspacing="0" cellpadding="0">',
34678                "<tbody>{rows}</tbody>",
34679                "</table>"
34680             );
34681             tpls.body.disableFormats = true;
34682         }
34683         tpls.body.compile();
34684
34685         if(!tpls.row){
34686             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
34687             tpls.row.disableFormats = true;
34688         }
34689         tpls.row.compile();
34690
34691         if(!tpls.cell){
34692             tpls.cell = new Roo.Template(
34693                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
34694                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
34695                 "</td>"
34696             );
34697             tpls.cell.disableFormats = true;
34698         }
34699         tpls.cell.compile();
34700
34701         this.templates = tpls;
34702     },
34703
34704     // remap these for backwards compat
34705     onColWidthChange : function(){
34706         this.updateColumns.apply(this, arguments);
34707     },
34708     onHeaderChange : function(){
34709         this.updateHeaders.apply(this, arguments);
34710     }, 
34711     onHiddenChange : function(){
34712         this.handleHiddenChange.apply(this, arguments);
34713     },
34714     onColumnMove : function(){
34715         this.handleColumnMove.apply(this, arguments);
34716     },
34717     onColumnLock : function(){
34718         this.handleLockChange.apply(this, arguments);
34719     },
34720
34721     onDataChange : function(){
34722         this.refresh();
34723         this.updateHeaderSortState();
34724     },
34725
34726     onClear : function(){
34727         this.refresh();
34728     },
34729
34730     onUpdate : function(ds, record){
34731         this.refreshRow(record);
34732     },
34733
34734     refreshRow : function(record){
34735         var ds = this.ds, index;
34736         if(typeof record == 'number'){
34737             index = record;
34738             record = ds.getAt(index);
34739         }else{
34740             index = ds.indexOf(record);
34741         }
34742         this.insertRows(ds, index, index, true);
34743         this.onRemove(ds, record, index+1, true);
34744         this.syncRowHeights(index, index);
34745         this.layout();
34746         this.fireEvent("rowupdated", this, index, record);
34747     },
34748
34749     onAdd : function(ds, records, index){
34750         this.insertRows(ds, index, index + (records.length-1));
34751     },
34752
34753     onRemove : function(ds, record, index, isUpdate){
34754         if(isUpdate !== true){
34755             this.fireEvent("beforerowremoved", this, index, record);
34756         }
34757         var bt = this.getBodyTable(), lt = this.getLockedTable();
34758         if(bt.rows[index]){
34759             bt.firstChild.removeChild(bt.rows[index]);
34760         }
34761         if(lt.rows[index]){
34762             lt.firstChild.removeChild(lt.rows[index]);
34763         }
34764         if(isUpdate !== true){
34765             this.stripeRows(index);
34766             this.syncRowHeights(index, index);
34767             this.layout();
34768             this.fireEvent("rowremoved", this, index, record);
34769         }
34770     },
34771
34772     onLoad : function(){
34773         this.scrollToTop();
34774     },
34775
34776     /**
34777      * Scrolls the grid to the top
34778      */
34779     scrollToTop : function(){
34780         if(this.scroller){
34781             this.scroller.dom.scrollTop = 0;
34782             this.syncScroll();
34783         }
34784     },
34785
34786     /**
34787      * Gets a panel in the header of the grid that can be used for toolbars etc.
34788      * After modifying the contents of this panel a call to grid.autoSize() may be
34789      * required to register any changes in size.
34790      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
34791      * @return Roo.Element
34792      */
34793     getHeaderPanel : function(doShow){
34794         if(doShow){
34795             this.headerPanel.show();
34796         }
34797         return this.headerPanel;
34798     },
34799
34800     /**
34801      * Gets a panel in the footer of the grid that can be used for toolbars etc.
34802      * After modifying the contents of this panel a call to grid.autoSize() may be
34803      * required to register any changes in size.
34804      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
34805      * @return Roo.Element
34806      */
34807     getFooterPanel : function(doShow){
34808         if(doShow){
34809             this.footerPanel.show();
34810         }
34811         return this.footerPanel;
34812     },
34813
34814     initElements : function(){
34815         var E = Roo.Element;
34816         var el = this.grid.getGridEl().dom.firstChild;
34817         var cs = el.childNodes;
34818
34819         this.el = new E(el);
34820         
34821          this.focusEl = new E(el.firstChild);
34822         this.focusEl.swallowEvent("click", true);
34823         
34824         this.headerPanel = new E(cs[1]);
34825         this.headerPanel.enableDisplayMode("block");
34826
34827         this.scroller = new E(cs[2]);
34828         this.scrollSizer = new E(this.scroller.dom.firstChild);
34829
34830         this.lockedWrap = new E(cs[3]);
34831         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
34832         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
34833
34834         this.mainWrap = new E(cs[4]);
34835         this.mainHd = new E(this.mainWrap.dom.firstChild);
34836         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
34837
34838         this.footerPanel = new E(cs[5]);
34839         this.footerPanel.enableDisplayMode("block");
34840
34841         this.resizeProxy = new E(cs[6]);
34842
34843         this.headerSelector = String.format(
34844            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
34845            this.lockedHd.id, this.mainHd.id
34846         );
34847
34848         this.splitterSelector = String.format(
34849            '#{0} div.x-grid-split, #{1} div.x-grid-split',
34850            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
34851         );
34852     },
34853     idToCssName : function(s)
34854     {
34855         return s.replace(/[^a-z0-9]+/ig, '-');
34856     },
34857
34858     getHeaderCell : function(index){
34859         return Roo.DomQuery.select(this.headerSelector)[index];
34860     },
34861
34862     getHeaderCellMeasure : function(index){
34863         return this.getHeaderCell(index).firstChild;
34864     },
34865
34866     getHeaderCellText : function(index){
34867         return this.getHeaderCell(index).firstChild.firstChild;
34868     },
34869
34870     getLockedTable : function(){
34871         return this.lockedBody.dom.firstChild;
34872     },
34873
34874     getBodyTable : function(){
34875         return this.mainBody.dom.firstChild;
34876     },
34877
34878     getLockedRow : function(index){
34879         return this.getLockedTable().rows[index];
34880     },
34881
34882     getRow : function(index){
34883         return this.getBodyTable().rows[index];
34884     },
34885
34886     getRowComposite : function(index){
34887         if(!this.rowEl){
34888             this.rowEl = new Roo.CompositeElementLite();
34889         }
34890         var els = [], lrow, mrow;
34891         if(lrow = this.getLockedRow(index)){
34892             els.push(lrow);
34893         }
34894         if(mrow = this.getRow(index)){
34895             els.push(mrow);
34896         }
34897         this.rowEl.elements = els;
34898         return this.rowEl;
34899     },
34900     /**
34901      * Gets the 'td' of the cell
34902      * 
34903      * @param {Integer} rowIndex row to select
34904      * @param {Integer} colIndex column to select
34905      * 
34906      * @return {Object} 
34907      */
34908     getCell : function(rowIndex, colIndex){
34909         var locked = this.cm.getLockedCount();
34910         var source;
34911         if(colIndex < locked){
34912             source = this.lockedBody.dom.firstChild;
34913         }else{
34914             source = this.mainBody.dom.firstChild;
34915             colIndex -= locked;
34916         }
34917         return source.rows[rowIndex].childNodes[colIndex];
34918     },
34919
34920     getCellText : function(rowIndex, colIndex){
34921         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
34922     },
34923
34924     getCellBox : function(cell){
34925         var b = this.fly(cell).getBox();
34926         if(Roo.isOpera){ // opera fails to report the Y
34927             b.y = cell.offsetTop + this.mainBody.getY();
34928         }
34929         return b;
34930     },
34931
34932     getCellIndex : function(cell){
34933         var id = String(cell.className).match(this.cellRE);
34934         if(id){
34935             return parseInt(id[1], 10);
34936         }
34937         return 0;
34938     },
34939
34940     findHeaderIndex : function(n){
34941         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
34942         return r ? this.getCellIndex(r) : false;
34943     },
34944
34945     findHeaderCell : function(n){
34946         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
34947         return r ? r : false;
34948     },
34949
34950     findRowIndex : function(n){
34951         if(!n){
34952             return false;
34953         }
34954         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
34955         return r ? r.rowIndex : false;
34956     },
34957
34958     findCellIndex : function(node){
34959         var stop = this.el.dom;
34960         while(node && node != stop){
34961             if(this.findRE.test(node.className)){
34962                 return this.getCellIndex(node);
34963             }
34964             node = node.parentNode;
34965         }
34966         return false;
34967     },
34968
34969     getColumnId : function(index){
34970         return this.cm.getColumnId(index);
34971     },
34972
34973     getSplitters : function()
34974     {
34975         if(this.splitterSelector){
34976            return Roo.DomQuery.select(this.splitterSelector);
34977         }else{
34978             return null;
34979       }
34980     },
34981
34982     getSplitter : function(index){
34983         return this.getSplitters()[index];
34984     },
34985
34986     onRowOver : function(e, t){
34987         var row;
34988         if((row = this.findRowIndex(t)) !== false){
34989             this.getRowComposite(row).addClass("x-grid-row-over");
34990         }
34991     },
34992
34993     onRowOut : function(e, t){
34994         var row;
34995         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
34996             this.getRowComposite(row).removeClass("x-grid-row-over");
34997         }
34998     },
34999
35000     renderHeaders : function(){
35001         var cm = this.cm;
35002         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
35003         var cb = [], lb = [], sb = [], lsb = [], p = {};
35004         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35005             p.cellId = "x-grid-hd-0-" + i;
35006             p.splitId = "x-grid-csplit-0-" + i;
35007             p.id = cm.getColumnId(i);
35008             p.title = cm.getColumnTooltip(i) || "";
35009             p.value = cm.getColumnHeader(i) || "";
35010             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
35011             if(!cm.isLocked(i)){
35012                 cb[cb.length] = ct.apply(p);
35013                 sb[sb.length] = st.apply(p);
35014             }else{
35015                 lb[lb.length] = ct.apply(p);
35016                 lsb[lsb.length] = st.apply(p);
35017             }
35018         }
35019         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
35020                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
35021     },
35022
35023     updateHeaders : function(){
35024         var html = this.renderHeaders();
35025         this.lockedHd.update(html[0]);
35026         this.mainHd.update(html[1]);
35027     },
35028
35029     /**
35030      * Focuses the specified row.
35031      * @param {Number} row The row index
35032      */
35033     focusRow : function(row)
35034     {
35035         //Roo.log('GridView.focusRow');
35036         var x = this.scroller.dom.scrollLeft;
35037         this.focusCell(row, 0, false);
35038         this.scroller.dom.scrollLeft = x;
35039     },
35040
35041     /**
35042      * Focuses the specified cell.
35043      * @param {Number} row The row index
35044      * @param {Number} col The column index
35045      * @param {Boolean} hscroll false to disable horizontal scrolling
35046      */
35047     focusCell : function(row, col, hscroll)
35048     {
35049         //Roo.log('GridView.focusCell');
35050         var el = this.ensureVisible(row, col, hscroll);
35051         this.focusEl.alignTo(el, "tl-tl");
35052         if(Roo.isGecko){
35053             this.focusEl.focus();
35054         }else{
35055             this.focusEl.focus.defer(1, this.focusEl);
35056         }
35057     },
35058
35059     /**
35060      * Scrolls the specified cell into view
35061      * @param {Number} row The row index
35062      * @param {Number} col The column index
35063      * @param {Boolean} hscroll false to disable horizontal scrolling
35064      */
35065     ensureVisible : function(row, col, hscroll)
35066     {
35067         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
35068         //return null; //disable for testing.
35069         if(typeof row != "number"){
35070             row = row.rowIndex;
35071         }
35072         if(row < 0 && row >= this.ds.getCount()){
35073             return  null;
35074         }
35075         col = (col !== undefined ? col : 0);
35076         var cm = this.grid.colModel;
35077         while(cm.isHidden(col)){
35078             col++;
35079         }
35080
35081         var el = this.getCell(row, col);
35082         if(!el){
35083             return null;
35084         }
35085         var c = this.scroller.dom;
35086
35087         var ctop = parseInt(el.offsetTop, 10);
35088         var cleft = parseInt(el.offsetLeft, 10);
35089         var cbot = ctop + el.offsetHeight;
35090         var cright = cleft + el.offsetWidth;
35091         
35092         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
35093         var stop = parseInt(c.scrollTop, 10);
35094         var sleft = parseInt(c.scrollLeft, 10);
35095         var sbot = stop + ch;
35096         var sright = sleft + c.clientWidth;
35097         /*
35098         Roo.log('GridView.ensureVisible:' +
35099                 ' ctop:' + ctop +
35100                 ' c.clientHeight:' + c.clientHeight +
35101                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
35102                 ' stop:' + stop +
35103                 ' cbot:' + cbot +
35104                 ' sbot:' + sbot +
35105                 ' ch:' + ch  
35106                 );
35107         */
35108         if(ctop < stop){
35109              c.scrollTop = ctop;
35110             //Roo.log("set scrolltop to ctop DISABLE?");
35111         }else if(cbot > sbot){
35112             //Roo.log("set scrolltop to cbot-ch");
35113             c.scrollTop = cbot-ch;
35114         }
35115         
35116         if(hscroll !== false){
35117             if(cleft < sleft){
35118                 c.scrollLeft = cleft;
35119             }else if(cright > sright){
35120                 c.scrollLeft = cright-c.clientWidth;
35121             }
35122         }
35123          
35124         return el;
35125     },
35126
35127     updateColumns : function(){
35128         this.grid.stopEditing();
35129         var cm = this.grid.colModel, colIds = this.getColumnIds();
35130         //var totalWidth = cm.getTotalWidth();
35131         var pos = 0;
35132         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35133             //if(cm.isHidden(i)) continue;
35134             var w = cm.getColumnWidth(i);
35135             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35136             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35137         }
35138         this.updateSplitters();
35139     },
35140
35141     generateRules : function(cm){
35142         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
35143         Roo.util.CSS.removeStyleSheet(rulesId);
35144         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35145             var cid = cm.getColumnId(i);
35146             var align = '';
35147             if(cm.config[i].align){
35148                 align = 'text-align:'+cm.config[i].align+';';
35149             }
35150             var hidden = '';
35151             if(cm.isHidden(i)){
35152                 hidden = 'display:none;';
35153             }
35154             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
35155             ruleBuf.push(
35156                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
35157                     this.hdSelector, cid, " {\n", align, width, "}\n",
35158                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
35159                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
35160         }
35161         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
35162     },
35163
35164     updateSplitters : function(){
35165         var cm = this.cm, s = this.getSplitters();
35166         if(s){ // splitters not created yet
35167             var pos = 0, locked = true;
35168             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35169                 if(cm.isHidden(i)) continue;
35170                 var w = cm.getColumnWidth(i); // make sure it's a number
35171                 if(!cm.isLocked(i) && locked){
35172                     pos = 0;
35173                     locked = false;
35174                 }
35175                 pos += w;
35176                 s[i].style.left = (pos-this.splitOffset) + "px";
35177             }
35178         }
35179     },
35180
35181     handleHiddenChange : function(colModel, colIndex, hidden){
35182         if(hidden){
35183             this.hideColumn(colIndex);
35184         }else{
35185             this.unhideColumn(colIndex);
35186         }
35187     },
35188
35189     hideColumn : function(colIndex){
35190         var cid = this.getColumnId(colIndex);
35191         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
35192         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
35193         if(Roo.isSafari){
35194             this.updateHeaders();
35195         }
35196         this.updateSplitters();
35197         this.layout();
35198     },
35199
35200     unhideColumn : function(colIndex){
35201         var cid = this.getColumnId(colIndex);
35202         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
35203         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
35204
35205         if(Roo.isSafari){
35206             this.updateHeaders();
35207         }
35208         this.updateSplitters();
35209         this.layout();
35210     },
35211
35212     insertRows : function(dm, firstRow, lastRow, isUpdate){
35213         if(firstRow == 0 && lastRow == dm.getCount()-1){
35214             this.refresh();
35215         }else{
35216             if(!isUpdate){
35217                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
35218             }
35219             var s = this.getScrollState();
35220             var markup = this.renderRows(firstRow, lastRow);
35221             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
35222             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
35223             this.restoreScroll(s);
35224             if(!isUpdate){
35225                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
35226                 this.syncRowHeights(firstRow, lastRow);
35227                 this.stripeRows(firstRow);
35228                 this.layout();
35229             }
35230         }
35231     },
35232
35233     bufferRows : function(markup, target, index){
35234         var before = null, trows = target.rows, tbody = target.tBodies[0];
35235         if(index < trows.length){
35236             before = trows[index];
35237         }
35238         var b = document.createElement("div");
35239         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
35240         var rows = b.firstChild.rows;
35241         for(var i = 0, len = rows.length; i < len; i++){
35242             if(before){
35243                 tbody.insertBefore(rows[0], before);
35244             }else{
35245                 tbody.appendChild(rows[0]);
35246             }
35247         }
35248         b.innerHTML = "";
35249         b = null;
35250     },
35251
35252     deleteRows : function(dm, firstRow, lastRow){
35253         if(dm.getRowCount()<1){
35254             this.fireEvent("beforerefresh", this);
35255             this.mainBody.update("");
35256             this.lockedBody.update("");
35257             this.fireEvent("refresh", this);
35258         }else{
35259             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
35260             var bt = this.getBodyTable();
35261             var tbody = bt.firstChild;
35262             var rows = bt.rows;
35263             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
35264                 tbody.removeChild(rows[firstRow]);
35265             }
35266             this.stripeRows(firstRow);
35267             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
35268         }
35269     },
35270
35271     updateRows : function(dataSource, firstRow, lastRow){
35272         var s = this.getScrollState();
35273         this.refresh();
35274         this.restoreScroll(s);
35275     },
35276
35277     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
35278         if(!noRefresh){
35279            this.refresh();
35280         }
35281         this.updateHeaderSortState();
35282     },
35283
35284     getScrollState : function(){
35285         
35286         var sb = this.scroller.dom;
35287         return {left: sb.scrollLeft, top: sb.scrollTop};
35288     },
35289
35290     stripeRows : function(startRow){
35291         if(!this.grid.stripeRows || this.ds.getCount() < 1){
35292             return;
35293         }
35294         startRow = startRow || 0;
35295         var rows = this.getBodyTable().rows;
35296         var lrows = this.getLockedTable().rows;
35297         var cls = ' x-grid-row-alt ';
35298         for(var i = startRow, len = rows.length; i < len; i++){
35299             var row = rows[i], lrow = lrows[i];
35300             var isAlt = ((i+1) % 2 == 0);
35301             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
35302             if(isAlt == hasAlt){
35303                 continue;
35304             }
35305             if(isAlt){
35306                 row.className += " x-grid-row-alt";
35307             }else{
35308                 row.className = row.className.replace("x-grid-row-alt", "");
35309             }
35310             if(lrow){
35311                 lrow.className = row.className;
35312             }
35313         }
35314     },
35315
35316     restoreScroll : function(state){
35317         //Roo.log('GridView.restoreScroll');
35318         var sb = this.scroller.dom;
35319         sb.scrollLeft = state.left;
35320         sb.scrollTop = state.top;
35321         this.syncScroll();
35322     },
35323
35324     syncScroll : function(){
35325         //Roo.log('GridView.syncScroll');
35326         var sb = this.scroller.dom;
35327         var sh = this.mainHd.dom;
35328         var bs = this.mainBody.dom;
35329         var lv = this.lockedBody.dom;
35330         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
35331         lv.scrollTop = bs.scrollTop = sb.scrollTop;
35332     },
35333
35334     handleScroll : function(e){
35335         this.syncScroll();
35336         var sb = this.scroller.dom;
35337         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
35338         e.stopEvent();
35339     },
35340
35341     handleWheel : function(e){
35342         var d = e.getWheelDelta();
35343         this.scroller.dom.scrollTop -= d*22;
35344         // set this here to prevent jumpy scrolling on large tables
35345         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
35346         e.stopEvent();
35347     },
35348
35349     renderRows : function(startRow, endRow){
35350         // pull in all the crap needed to render rows
35351         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
35352         var colCount = cm.getColumnCount();
35353
35354         if(ds.getCount() < 1){
35355             return ["", ""];
35356         }
35357
35358         // build a map for all the columns
35359         var cs = [];
35360         for(var i = 0; i < colCount; i++){
35361             var name = cm.getDataIndex(i);
35362             cs[i] = {
35363                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
35364                 renderer : cm.getRenderer(i),
35365                 id : cm.getColumnId(i),
35366                 locked : cm.isLocked(i)
35367             };
35368         }
35369
35370         startRow = startRow || 0;
35371         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
35372
35373         // records to render
35374         var rs = ds.getRange(startRow, endRow);
35375
35376         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
35377     },
35378
35379     // As much as I hate to duplicate code, this was branched because FireFox really hates
35380     // [].join("") on strings. The performance difference was substantial enough to
35381     // branch this function
35382     doRender : Roo.isGecko ?
35383             function(cs, rs, ds, startRow, colCount, stripe){
35384                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35385                 // buffers
35386                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35387                 
35388                 var hasListener = this.grid.hasListener('rowclass');
35389                 var rowcfg = {};
35390                 for(var j = 0, len = rs.length; j < len; j++){
35391                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
35392                     for(var i = 0; i < colCount; i++){
35393                         c = cs[i];
35394                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35395                         p.id = c.id;
35396                         p.css = p.attr = "";
35397                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35398                         if(p.value == undefined || p.value === "") p.value = "&#160;";
35399                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35400                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
35401                         }
35402                         var markup = ct.apply(p);
35403                         if(!c.locked){
35404                             cb+= markup;
35405                         }else{
35406                             lcb+= markup;
35407                         }
35408                     }
35409                     var alt = [];
35410                     if(stripe && ((rowIndex+1) % 2 == 0)){
35411                         alt.push("x-grid-row-alt")
35412                     }
35413                     if(r.dirty){
35414                         alt.push(  " x-grid-dirty-row");
35415                     }
35416                     rp.cells = lcb;
35417                     if(this.getRowClass){
35418                         alt.push(this.getRowClass(r, rowIndex));
35419                     }
35420                     if (hasListener) {
35421                         rowcfg = {
35422                              
35423                             record: r,
35424                             rowIndex : rowIndex,
35425                             rowClass : ''
35426                         }
35427                         this.grid.fireEvent('rowclass', this, rowcfg);
35428                         alt.push(rowcfg.rowClass);
35429                     }
35430                     rp.alt = alt.join(" ");
35431                     lbuf+= rt.apply(rp);
35432                     rp.cells = cb;
35433                     buf+=  rt.apply(rp);
35434                 }
35435                 return [lbuf, buf];
35436             } :
35437             function(cs, rs, ds, startRow, colCount, stripe){
35438                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35439                 // buffers
35440                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35441                 var hasListener = this.grid.hasListener('rowclass');
35442  
35443                 var rowcfg = {};
35444                 for(var j = 0, len = rs.length; j < len; j++){
35445                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
35446                     for(var i = 0; i < colCount; i++){
35447                         c = cs[i];
35448                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35449                         p.id = c.id;
35450                         p.css = p.attr = "";
35451                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35452                         if(p.value == undefined || p.value === "") p.value = "&#160;";
35453                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35454                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
35455                         }
35456                         
35457                         var markup = ct.apply(p);
35458                         if(!c.locked){
35459                             cb[cb.length] = markup;
35460                         }else{
35461                             lcb[lcb.length] = markup;
35462                         }
35463                     }
35464                     var alt = [];
35465                     if(stripe && ((rowIndex+1) % 2 == 0)){
35466                         alt.push( "x-grid-row-alt");
35467                     }
35468                     if(r.dirty){
35469                         alt.push(" x-grid-dirty-row");
35470                     }
35471                     rp.cells = lcb;
35472                     if(this.getRowClass){
35473                         alt.push( this.getRowClass(r, rowIndex));
35474                     }
35475                     if (hasListener) {
35476                         rowcfg = {
35477                              
35478                             record: r,
35479                             rowIndex : rowIndex,
35480                             rowClass : ''
35481                         }
35482                         this.grid.fireEvent('rowclass', this, rowcfg);
35483                         alt.push(rowcfg.rowClass);
35484                     }
35485                     rp.alt = alt.join(" ");
35486                     rp.cells = lcb.join("");
35487                     lbuf[lbuf.length] = rt.apply(rp);
35488                     rp.cells = cb.join("");
35489                     buf[buf.length] =  rt.apply(rp);
35490                 }
35491                 return [lbuf.join(""), buf.join("")];
35492             },
35493
35494     renderBody : function(){
35495         var markup = this.renderRows();
35496         var bt = this.templates.body;
35497         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
35498     },
35499
35500     /**
35501      * Refreshes the grid
35502      * @param {Boolean} headersToo
35503      */
35504     refresh : function(headersToo){
35505         this.fireEvent("beforerefresh", this);
35506         this.grid.stopEditing();
35507         var result = this.renderBody();
35508         this.lockedBody.update(result[0]);
35509         this.mainBody.update(result[1]);
35510         if(headersToo === true){
35511             this.updateHeaders();
35512             this.updateColumns();
35513             this.updateSplitters();
35514             this.updateHeaderSortState();
35515         }
35516         this.syncRowHeights();
35517         this.layout();
35518         this.fireEvent("refresh", this);
35519     },
35520
35521     handleColumnMove : function(cm, oldIndex, newIndex){
35522         this.indexMap = null;
35523         var s = this.getScrollState();
35524         this.refresh(true);
35525         this.restoreScroll(s);
35526         this.afterMove(newIndex);
35527     },
35528
35529     afterMove : function(colIndex){
35530         if(this.enableMoveAnim && Roo.enableFx){
35531             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
35532         }
35533         // if multisort - fix sortOrder, and reload..
35534         if (this.grid.dataSource.multiSort) {
35535             // the we can call sort again..
35536             var dm = this.grid.dataSource;
35537             var cm = this.grid.colModel;
35538             var so = [];
35539             for(var i = 0; i < cm.config.length; i++ ) {
35540                 
35541                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
35542                     continue; // dont' bother, it's not in sort list or being set.
35543                 }
35544                 
35545                 so.push(cm.config[i].dataIndex);
35546             };
35547             dm.sortOrder = so;
35548             dm.load(dm.lastOptions);
35549             
35550             
35551         }
35552         
35553     },
35554
35555     updateCell : function(dm, rowIndex, dataIndex){
35556         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
35557         if(typeof colIndex == "undefined"){ // not present in grid
35558             return;
35559         }
35560         var cm = this.grid.colModel;
35561         var cell = this.getCell(rowIndex, colIndex);
35562         var cellText = this.getCellText(rowIndex, colIndex);
35563
35564         var p = {
35565             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
35566             id : cm.getColumnId(colIndex),
35567             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
35568         };
35569         var renderer = cm.getRenderer(colIndex);
35570         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
35571         if(typeof val == "undefined" || val === "") val = "&#160;";
35572         cellText.innerHTML = val;
35573         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
35574         this.syncRowHeights(rowIndex, rowIndex);
35575     },
35576
35577     calcColumnWidth : function(colIndex, maxRowsToMeasure){
35578         var maxWidth = 0;
35579         if(this.grid.autoSizeHeaders){
35580             var h = this.getHeaderCellMeasure(colIndex);
35581             maxWidth = Math.max(maxWidth, h.scrollWidth);
35582         }
35583         var tb, index;
35584         if(this.cm.isLocked(colIndex)){
35585             tb = this.getLockedTable();
35586             index = colIndex;
35587         }else{
35588             tb = this.getBodyTable();
35589             index = colIndex - this.cm.getLockedCount();
35590         }
35591         if(tb && tb.rows){
35592             var rows = tb.rows;
35593             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
35594             for(var i = 0; i < stopIndex; i++){
35595                 var cell = rows[i].childNodes[index].firstChild;
35596                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
35597             }
35598         }
35599         return maxWidth + /*margin for error in IE*/ 5;
35600     },
35601     /**
35602      * Autofit a column to its content.
35603      * @param {Number} colIndex
35604      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
35605      */
35606      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
35607          if(this.cm.isHidden(colIndex)){
35608              return; // can't calc a hidden column
35609          }
35610         if(forceMinSize){
35611             var cid = this.cm.getColumnId(colIndex);
35612             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
35613            if(this.grid.autoSizeHeaders){
35614                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
35615            }
35616         }
35617         var newWidth = this.calcColumnWidth(colIndex);
35618         this.cm.setColumnWidth(colIndex,
35619             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
35620         if(!suppressEvent){
35621             this.grid.fireEvent("columnresize", colIndex, newWidth);
35622         }
35623     },
35624
35625     /**
35626      * Autofits all columns to their content and then expands to fit any extra space in the grid
35627      */
35628      autoSizeColumns : function(){
35629         var cm = this.grid.colModel;
35630         var colCount = cm.getColumnCount();
35631         for(var i = 0; i < colCount; i++){
35632             this.autoSizeColumn(i, true, true);
35633         }
35634         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
35635             this.fitColumns();
35636         }else{
35637             this.updateColumns();
35638             this.layout();
35639         }
35640     },
35641
35642     /**
35643      * Autofits all columns to the grid's width proportionate with their current size
35644      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
35645      */
35646     fitColumns : function(reserveScrollSpace){
35647         var cm = this.grid.colModel;
35648         var colCount = cm.getColumnCount();
35649         var cols = [];
35650         var width = 0;
35651         var i, w;
35652         for (i = 0; i < colCount; i++){
35653             if(!cm.isHidden(i) && !cm.isFixed(i)){
35654                 w = cm.getColumnWidth(i);
35655                 cols.push(i);
35656                 cols.push(w);
35657                 width += w;
35658             }
35659         }
35660         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
35661         if(reserveScrollSpace){
35662             avail -= 17;
35663         }
35664         var frac = (avail - cm.getTotalWidth())/width;
35665         while (cols.length){
35666             w = cols.pop();
35667             i = cols.pop();
35668             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
35669         }
35670         this.updateColumns();
35671         this.layout();
35672     },
35673
35674     onRowSelect : function(rowIndex){
35675         var row = this.getRowComposite(rowIndex);
35676         row.addClass("x-grid-row-selected");
35677     },
35678
35679     onRowDeselect : function(rowIndex){
35680         var row = this.getRowComposite(rowIndex);
35681         row.removeClass("x-grid-row-selected");
35682     },
35683
35684     onCellSelect : function(row, col){
35685         var cell = this.getCell(row, col);
35686         if(cell){
35687             Roo.fly(cell).addClass("x-grid-cell-selected");
35688         }
35689     },
35690
35691     onCellDeselect : function(row, col){
35692         var cell = this.getCell(row, col);
35693         if(cell){
35694             Roo.fly(cell).removeClass("x-grid-cell-selected");
35695         }
35696     },
35697
35698     updateHeaderSortState : function(){
35699         
35700         // sort state can be single { field: xxx, direction : yyy}
35701         // or   { xxx=>ASC , yyy : DESC ..... }
35702         
35703         var mstate = {};
35704         if (!this.ds.multiSort) { 
35705             var state = this.ds.getSortState();
35706             if(!state){
35707                 return;
35708             }
35709             mstate[state.field] = state.direction;
35710             // FIXME... - this is not used here.. but might be elsewhere..
35711             this.sortState = state;
35712             
35713         } else {
35714             mstate = this.ds.sortToggle;
35715         }
35716         //remove existing sort classes..
35717         
35718         var sc = this.sortClasses;
35719         var hds = this.el.select(this.headerSelector).removeClass(sc);
35720         
35721         for(var f in mstate) {
35722         
35723             var sortColumn = this.cm.findColumnIndex(f);
35724             
35725             if(sortColumn != -1){
35726                 var sortDir = mstate[f];        
35727                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
35728             }
35729         }
35730         
35731          
35732         
35733     },
35734
35735
35736     handleHeaderClick : function(g, index){
35737         if(this.headersDisabled){
35738             return;
35739         }
35740         var dm = g.dataSource, cm = g.colModel;
35741         if(!cm.isSortable(index)){
35742             return;
35743         }
35744         g.stopEditing();
35745         
35746         if (dm.multiSort) {
35747             // update the sortOrder
35748             var so = [];
35749             for(var i = 0; i < cm.config.length; i++ ) {
35750                 
35751                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
35752                     continue; // dont' bother, it's not in sort list or being set.
35753                 }
35754                 
35755                 so.push(cm.config[i].dataIndex);
35756             };
35757             dm.sortOrder = so;
35758         }
35759         
35760         
35761         dm.sort(cm.getDataIndex(index));
35762     },
35763
35764
35765     destroy : function(){
35766         if(this.colMenu){
35767             this.colMenu.removeAll();
35768             Roo.menu.MenuMgr.unregister(this.colMenu);
35769             this.colMenu.getEl().remove();
35770             delete this.colMenu;
35771         }
35772         if(this.hmenu){
35773             this.hmenu.removeAll();
35774             Roo.menu.MenuMgr.unregister(this.hmenu);
35775             this.hmenu.getEl().remove();
35776             delete this.hmenu;
35777         }
35778         if(this.grid.enableColumnMove){
35779             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35780             if(dds){
35781                 for(var dd in dds){
35782                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
35783                         var elid = dds[dd].dragElId;
35784                         dds[dd].unreg();
35785                         Roo.get(elid).remove();
35786                     } else if(dds[dd].config.isTarget){
35787                         dds[dd].proxyTop.remove();
35788                         dds[dd].proxyBottom.remove();
35789                         dds[dd].unreg();
35790                     }
35791                     if(Roo.dd.DDM.locationCache[dd]){
35792                         delete Roo.dd.DDM.locationCache[dd];
35793                     }
35794                 }
35795                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35796             }
35797         }
35798         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
35799         this.bind(null, null);
35800         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
35801     },
35802
35803     handleLockChange : function(){
35804         this.refresh(true);
35805     },
35806
35807     onDenyColumnLock : function(){
35808
35809     },
35810
35811     onDenyColumnHide : function(){
35812
35813     },
35814
35815     handleHdMenuClick : function(item){
35816         var index = this.hdCtxIndex;
35817         var cm = this.cm, ds = this.ds;
35818         switch(item.id){
35819             case "asc":
35820                 ds.sort(cm.getDataIndex(index), "ASC");
35821                 break;
35822             case "desc":
35823                 ds.sort(cm.getDataIndex(index), "DESC");
35824                 break;
35825             case "lock":
35826                 var lc = cm.getLockedCount();
35827                 if(cm.getColumnCount(true) <= lc+1){
35828                     this.onDenyColumnLock();
35829                     return;
35830                 }
35831                 if(lc != index){
35832                     cm.setLocked(index, true, true);
35833                     cm.moveColumn(index, lc);
35834                     this.grid.fireEvent("columnmove", index, lc);
35835                 }else{
35836                     cm.setLocked(index, true);
35837                 }
35838             break;
35839             case "unlock":
35840                 var lc = cm.getLockedCount();
35841                 if((lc-1) != index){
35842                     cm.setLocked(index, false, true);
35843                     cm.moveColumn(index, lc-1);
35844                     this.grid.fireEvent("columnmove", index, lc-1);
35845                 }else{
35846                     cm.setLocked(index, false);
35847                 }
35848             break;
35849             default:
35850                 index = cm.getIndexById(item.id.substr(4));
35851                 if(index != -1){
35852                     if(item.checked && cm.getColumnCount(true) <= 1){
35853                         this.onDenyColumnHide();
35854                         return false;
35855                     }
35856                     cm.setHidden(index, item.checked);
35857                 }
35858         }
35859         return true;
35860     },
35861
35862     beforeColMenuShow : function(){
35863         var cm = this.cm,  colCount = cm.getColumnCount();
35864         this.colMenu.removeAll();
35865         for(var i = 0; i < colCount; i++){
35866             this.colMenu.add(new Roo.menu.CheckItem({
35867                 id: "col-"+cm.getColumnId(i),
35868                 text: cm.getColumnHeader(i),
35869                 checked: !cm.isHidden(i),
35870                 hideOnClick:false
35871             }));
35872         }
35873     },
35874
35875     handleHdCtx : function(g, index, e){
35876         e.stopEvent();
35877         var hd = this.getHeaderCell(index);
35878         this.hdCtxIndex = index;
35879         var ms = this.hmenu.items, cm = this.cm;
35880         ms.get("asc").setDisabled(!cm.isSortable(index));
35881         ms.get("desc").setDisabled(!cm.isSortable(index));
35882         if(this.grid.enableColLock !== false){
35883             ms.get("lock").setDisabled(cm.isLocked(index));
35884             ms.get("unlock").setDisabled(!cm.isLocked(index));
35885         }
35886         this.hmenu.show(hd, "tl-bl");
35887     },
35888
35889     handleHdOver : function(e){
35890         var hd = this.findHeaderCell(e.getTarget());
35891         if(hd && !this.headersDisabled){
35892             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
35893                this.fly(hd).addClass("x-grid-hd-over");
35894             }
35895         }
35896     },
35897
35898     handleHdOut : function(e){
35899         var hd = this.findHeaderCell(e.getTarget());
35900         if(hd){
35901             this.fly(hd).removeClass("x-grid-hd-over");
35902         }
35903     },
35904
35905     handleSplitDblClick : function(e, t){
35906         var i = this.getCellIndex(t);
35907         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
35908             this.autoSizeColumn(i, true);
35909             this.layout();
35910         }
35911     },
35912
35913     render : function(){
35914
35915         var cm = this.cm;
35916         var colCount = cm.getColumnCount();
35917
35918         if(this.grid.monitorWindowResize === true){
35919             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35920         }
35921         var header = this.renderHeaders();
35922         var body = this.templates.body.apply({rows:""});
35923         var html = this.templates.master.apply({
35924             lockedBody: body,
35925             body: body,
35926             lockedHeader: header[0],
35927             header: header[1]
35928         });
35929
35930         //this.updateColumns();
35931
35932         this.grid.getGridEl().dom.innerHTML = html;
35933
35934         this.initElements();
35935         
35936         // a kludge to fix the random scolling effect in webkit
35937         this.el.on("scroll", function() {
35938             this.el.dom.scrollTop=0; // hopefully not recursive..
35939         },this);
35940
35941         this.scroller.on("scroll", this.handleScroll, this);
35942         this.lockedBody.on("mousewheel", this.handleWheel, this);
35943         this.mainBody.on("mousewheel", this.handleWheel, this);
35944
35945         this.mainHd.on("mouseover", this.handleHdOver, this);
35946         this.mainHd.on("mouseout", this.handleHdOut, this);
35947         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
35948                 {delegate: "."+this.splitClass});
35949
35950         this.lockedHd.on("mouseover", this.handleHdOver, this);
35951         this.lockedHd.on("mouseout", this.handleHdOut, this);
35952         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
35953                 {delegate: "."+this.splitClass});
35954
35955         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
35956             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35957         }
35958
35959         this.updateSplitters();
35960
35961         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
35962             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35963             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35964         }
35965
35966         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
35967             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
35968             this.hmenu.add(
35969                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
35970                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
35971             );
35972             if(this.grid.enableColLock !== false){
35973                 this.hmenu.add('-',
35974                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
35975                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
35976                 );
35977             }
35978             if(this.grid.enableColumnHide !== false){
35979
35980                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
35981                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
35982                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
35983
35984                 this.hmenu.add('-',
35985                     {id:"columns", text: this.columnsText, menu: this.colMenu}
35986                 );
35987             }
35988             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
35989
35990             this.grid.on("headercontextmenu", this.handleHdCtx, this);
35991         }
35992
35993         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
35994             this.dd = new Roo.grid.GridDragZone(this.grid, {
35995                 ddGroup : this.grid.ddGroup || 'GridDD'
35996             });
35997         }
35998
35999         /*
36000         for(var i = 0; i < colCount; i++){
36001             if(cm.isHidden(i)){
36002                 this.hideColumn(i);
36003             }
36004             if(cm.config[i].align){
36005                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
36006                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
36007             }
36008         }*/
36009         
36010         this.updateHeaderSortState();
36011
36012         this.beforeInitialResize();
36013         this.layout(true);
36014
36015         // two part rendering gives faster view to the user
36016         this.renderPhase2.defer(1, this);
36017     },
36018
36019     renderPhase2 : function(){
36020         // render the rows now
36021         this.refresh();
36022         if(this.grid.autoSizeColumns){
36023             this.autoSizeColumns();
36024         }
36025     },
36026
36027     beforeInitialResize : function(){
36028
36029     },
36030
36031     onColumnSplitterMoved : function(i, w){
36032         this.userResized = true;
36033         var cm = this.grid.colModel;
36034         cm.setColumnWidth(i, w, true);
36035         var cid = cm.getColumnId(i);
36036         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36037         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36038         this.updateSplitters();
36039         this.layout();
36040         this.grid.fireEvent("columnresize", i, w);
36041     },
36042
36043     syncRowHeights : function(startIndex, endIndex){
36044         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
36045             startIndex = startIndex || 0;
36046             var mrows = this.getBodyTable().rows;
36047             var lrows = this.getLockedTable().rows;
36048             var len = mrows.length-1;
36049             endIndex = Math.min(endIndex || len, len);
36050             for(var i = startIndex; i <= endIndex; i++){
36051                 var m = mrows[i], l = lrows[i];
36052                 var h = Math.max(m.offsetHeight, l.offsetHeight);
36053                 m.style.height = l.style.height = h + "px";
36054             }
36055         }
36056     },
36057
36058     layout : function(initialRender, is2ndPass){
36059         var g = this.grid;
36060         var auto = g.autoHeight;
36061         var scrollOffset = 16;
36062         var c = g.getGridEl(), cm = this.cm,
36063                 expandCol = g.autoExpandColumn,
36064                 gv = this;
36065         //c.beginMeasure();
36066
36067         if(!c.dom.offsetWidth){ // display:none?
36068             if(initialRender){
36069                 this.lockedWrap.show();
36070                 this.mainWrap.show();
36071             }
36072             return;
36073         }
36074
36075         var hasLock = this.cm.isLocked(0);
36076
36077         var tbh = this.headerPanel.getHeight();
36078         var bbh = this.footerPanel.getHeight();
36079
36080         if(auto){
36081             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
36082             var newHeight = ch + c.getBorderWidth("tb");
36083             if(g.maxHeight){
36084                 newHeight = Math.min(g.maxHeight, newHeight);
36085             }
36086             c.setHeight(newHeight);
36087         }
36088
36089         if(g.autoWidth){
36090             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
36091         }
36092
36093         var s = this.scroller;
36094
36095         var csize = c.getSize(true);
36096
36097         this.el.setSize(csize.width, csize.height);
36098
36099         this.headerPanel.setWidth(csize.width);
36100         this.footerPanel.setWidth(csize.width);
36101
36102         var hdHeight = this.mainHd.getHeight();
36103         var vw = csize.width;
36104         var vh = csize.height - (tbh + bbh);
36105
36106         s.setSize(vw, vh);
36107
36108         var bt = this.getBodyTable();
36109         var ltWidth = hasLock ?
36110                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
36111
36112         var scrollHeight = bt.offsetHeight;
36113         var scrollWidth = ltWidth + bt.offsetWidth;
36114         var vscroll = false, hscroll = false;
36115
36116         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
36117
36118         var lw = this.lockedWrap, mw = this.mainWrap;
36119         var lb = this.lockedBody, mb = this.mainBody;
36120
36121         setTimeout(function(){
36122             var t = s.dom.offsetTop;
36123             var w = s.dom.clientWidth,
36124                 h = s.dom.clientHeight;
36125
36126             lw.setTop(t);
36127             lw.setSize(ltWidth, h);
36128
36129             mw.setLeftTop(ltWidth, t);
36130             mw.setSize(w-ltWidth, h);
36131
36132             lb.setHeight(h-hdHeight);
36133             mb.setHeight(h-hdHeight);
36134
36135             if(is2ndPass !== true && !gv.userResized && expandCol){
36136                 // high speed resize without full column calculation
36137                 
36138                 var ci = cm.getIndexById(expandCol);
36139                 if (ci < 0) {
36140                     ci = cm.findColumnIndex(expandCol);
36141                 }
36142                 ci = Math.max(0, ci); // make sure it's got at least the first col.
36143                 var expandId = cm.getColumnId(ci);
36144                 var  tw = cm.getTotalWidth(false);
36145                 var currentWidth = cm.getColumnWidth(ci);
36146                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
36147                 if(currentWidth != cw){
36148                     cm.setColumnWidth(ci, cw, true);
36149                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36150                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36151                     gv.updateSplitters();
36152                     gv.layout(false, true);
36153                 }
36154             }
36155
36156             if(initialRender){
36157                 lw.show();
36158                 mw.show();
36159             }
36160             //c.endMeasure();
36161         }, 10);
36162     },
36163
36164     onWindowResize : function(){
36165         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
36166             return;
36167         }
36168         this.layout();
36169     },
36170
36171     appendFooter : function(parentEl){
36172         return null;
36173     },
36174
36175     sortAscText : "Sort Ascending",
36176     sortDescText : "Sort Descending",
36177     lockText : "Lock Column",
36178     unlockText : "Unlock Column",
36179     columnsText : "Columns"
36180 });
36181
36182
36183 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
36184     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
36185     this.proxy.el.addClass('x-grid3-col-dd');
36186 };
36187
36188 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
36189     handleMouseDown : function(e){
36190
36191     },
36192
36193     callHandleMouseDown : function(e){
36194         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
36195     }
36196 });
36197 /*
36198  * Based on:
36199  * Ext JS Library 1.1.1
36200  * Copyright(c) 2006-2007, Ext JS, LLC.
36201  *
36202  * Originally Released Under LGPL - original licence link has changed is not relivant.
36203  *
36204  * Fork - LGPL
36205  * <script type="text/javascript">
36206  */
36207  
36208 // private
36209 // This is a support class used internally by the Grid components
36210 Roo.grid.SplitDragZone = function(grid, hd, hd2){
36211     this.grid = grid;
36212     this.view = grid.getView();
36213     this.proxy = this.view.resizeProxy;
36214     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
36215         "gridSplitters" + this.grid.getGridEl().id, {
36216         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
36217     });
36218     this.setHandleElId(Roo.id(hd));
36219     this.setOuterHandleElId(Roo.id(hd2));
36220     this.scroll = false;
36221 };
36222 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
36223     fly: Roo.Element.fly,
36224
36225     b4StartDrag : function(x, y){
36226         this.view.headersDisabled = true;
36227         this.proxy.setHeight(this.view.mainWrap.getHeight());
36228         var w = this.cm.getColumnWidth(this.cellIndex);
36229         var minw = Math.max(w-this.grid.minColumnWidth, 0);
36230         this.resetConstraints();
36231         this.setXConstraint(minw, 1000);
36232         this.setYConstraint(0, 0);
36233         this.minX = x - minw;
36234         this.maxX = x + 1000;
36235         this.startPos = x;
36236         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
36237     },
36238
36239
36240     handleMouseDown : function(e){
36241         ev = Roo.EventObject.setEvent(e);
36242         var t = this.fly(ev.getTarget());
36243         if(t.hasClass("x-grid-split")){
36244             this.cellIndex = this.view.getCellIndex(t.dom);
36245             this.split = t.dom;
36246             this.cm = this.grid.colModel;
36247             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
36248                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
36249             }
36250         }
36251     },
36252
36253     endDrag : function(e){
36254         this.view.headersDisabled = false;
36255         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
36256         var diff = endX - this.startPos;
36257         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
36258     },
36259
36260     autoOffset : function(){
36261         this.setDelta(0,0);
36262     }
36263 });/*
36264  * Based on:
36265  * Ext JS Library 1.1.1
36266  * Copyright(c) 2006-2007, Ext JS, LLC.
36267  *
36268  * Originally Released Under LGPL - original licence link has changed is not relivant.
36269  *
36270  * Fork - LGPL
36271  * <script type="text/javascript">
36272  */
36273  
36274 // private
36275 // This is a support class used internally by the Grid components
36276 Roo.grid.GridDragZone = function(grid, config){
36277     this.view = grid.getView();
36278     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
36279     if(this.view.lockedBody){
36280         this.setHandleElId(Roo.id(this.view.mainBody.dom));
36281         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
36282     }
36283     this.scroll = false;
36284     this.grid = grid;
36285     this.ddel = document.createElement('div');
36286     this.ddel.className = 'x-grid-dd-wrap';
36287 };
36288
36289 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
36290     ddGroup : "GridDD",
36291
36292     getDragData : function(e){
36293         var t = Roo.lib.Event.getTarget(e);
36294         var rowIndex = this.view.findRowIndex(t);
36295         if(rowIndex !== false){
36296             var sm = this.grid.selModel;
36297             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
36298               //  sm.mouseDown(e, t);
36299             //}
36300             if (e.hasModifier()){
36301                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
36302             }
36303             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
36304         }
36305         return false;
36306     },
36307
36308     onInitDrag : function(e){
36309         var data = this.dragData;
36310         this.ddel.innerHTML = this.grid.getDragDropText();
36311         this.proxy.update(this.ddel);
36312         // fire start drag?
36313     },
36314
36315     afterRepair : function(){
36316         this.dragging = false;
36317     },
36318
36319     getRepairXY : function(e, data){
36320         return false;
36321     },
36322
36323     onEndDrag : function(data, e){
36324         // fire end drag?
36325     },
36326
36327     onValidDrop : function(dd, e, id){
36328         // fire drag drop?
36329         this.hideProxy();
36330     },
36331
36332     beforeInvalidDrop : function(e, id){
36333
36334     }
36335 });/*
36336  * Based on:
36337  * Ext JS Library 1.1.1
36338  * Copyright(c) 2006-2007, Ext JS, LLC.
36339  *
36340  * Originally Released Under LGPL - original licence link has changed is not relivant.
36341  *
36342  * Fork - LGPL
36343  * <script type="text/javascript">
36344  */
36345  
36346
36347 /**
36348  * @class Roo.grid.ColumnModel
36349  * @extends Roo.util.Observable
36350  * This is the default implementation of a ColumnModel used by the Grid. It defines
36351  * the columns in the grid.
36352  * <br>Usage:<br>
36353  <pre><code>
36354  var colModel = new Roo.grid.ColumnModel([
36355         {header: "Ticker", width: 60, sortable: true, locked: true},
36356         {header: "Company Name", width: 150, sortable: true},
36357         {header: "Market Cap.", width: 100, sortable: true},
36358         {header: "$ Sales", width: 100, sortable: true, renderer: money},
36359         {header: "Employees", width: 100, sortable: true, resizable: false}
36360  ]);
36361  </code></pre>
36362  * <p>
36363  
36364  * The config options listed for this class are options which may appear in each
36365  * individual column definition.
36366  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
36367  * @constructor
36368  * @param {Object} config An Array of column config objects. See this class's
36369  * config objects for details.
36370 */
36371 Roo.grid.ColumnModel = function(config){
36372         /**
36373      * The config passed into the constructor
36374      */
36375     this.config = config;
36376     this.lookup = {};
36377
36378     // if no id, create one
36379     // if the column does not have a dataIndex mapping,
36380     // map it to the order it is in the config
36381     for(var i = 0, len = config.length; i < len; i++){
36382         var c = config[i];
36383         if(typeof c.dataIndex == "undefined"){
36384             c.dataIndex = i;
36385         }
36386         if(typeof c.renderer == "string"){
36387             c.renderer = Roo.util.Format[c.renderer];
36388         }
36389         if(typeof c.id == "undefined"){
36390             c.id = Roo.id();
36391         }
36392         if(c.editor && c.editor.xtype){
36393             c.editor  = Roo.factory(c.editor, Roo.grid);
36394         }
36395         if(c.editor && c.editor.isFormField){
36396             c.editor = new Roo.grid.GridEditor(c.editor);
36397         }
36398         this.lookup[c.id] = c;
36399     }
36400
36401     /**
36402      * The width of columns which have no width specified (defaults to 100)
36403      * @type Number
36404      */
36405     this.defaultWidth = 100;
36406
36407     /**
36408      * Default sortable of columns which have no sortable specified (defaults to false)
36409      * @type Boolean
36410      */
36411     this.defaultSortable = false;
36412
36413     this.addEvents({
36414         /**
36415              * @event widthchange
36416              * Fires when the width of a column changes.
36417              * @param {ColumnModel} this
36418              * @param {Number} columnIndex The column index
36419              * @param {Number} newWidth The new width
36420              */
36421             "widthchange": true,
36422         /**
36423              * @event headerchange
36424              * Fires when the text of a header changes.
36425              * @param {ColumnModel} this
36426              * @param {Number} columnIndex The column index
36427              * @param {Number} newText The new header text
36428              */
36429             "headerchange": true,
36430         /**
36431              * @event hiddenchange
36432              * Fires when a column is hidden or "unhidden".
36433              * @param {ColumnModel} this
36434              * @param {Number} columnIndex The column index
36435              * @param {Boolean} hidden true if hidden, false otherwise
36436              */
36437             "hiddenchange": true,
36438             /**
36439          * @event columnmoved
36440          * Fires when a column is moved.
36441          * @param {ColumnModel} this
36442          * @param {Number} oldIndex
36443          * @param {Number} newIndex
36444          */
36445         "columnmoved" : true,
36446         /**
36447          * @event columlockchange
36448          * Fires when a column's locked state is changed
36449          * @param {ColumnModel} this
36450          * @param {Number} colIndex
36451          * @param {Boolean} locked true if locked
36452          */
36453         "columnlockchange" : true
36454     });
36455     Roo.grid.ColumnModel.superclass.constructor.call(this);
36456 };
36457 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
36458     /**
36459      * @cfg {String} header The header text to display in the Grid view.
36460      */
36461     /**
36462      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
36463      * {@link Roo.data.Record} definition from which to draw the column's value. If not
36464      * specified, the column's index is used as an index into the Record's data Array.
36465      */
36466     /**
36467      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
36468      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
36469      */
36470     /**
36471      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
36472      * Defaults to the value of the {@link #defaultSortable} property.
36473      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
36474      */
36475     /**
36476      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
36477      */
36478     /**
36479      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
36480      */
36481     /**
36482      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
36483      */
36484     /**
36485      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
36486      */
36487     /**
36488      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
36489      * given the cell's data value. See {@link #setRenderer}. If not specified, the
36490      * default renderer uses the raw data value.
36491      */
36492        /**
36493      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
36494      */
36495     /**
36496      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
36497      */
36498
36499     /**
36500      * Returns the id of the column at the specified index.
36501      * @param {Number} index The column index
36502      * @return {String} the id
36503      */
36504     getColumnId : function(index){
36505         return this.config[index].id;
36506     },
36507
36508     /**
36509      * Returns the column for a specified id.
36510      * @param {String} id The column id
36511      * @return {Object} the column
36512      */
36513     getColumnById : function(id){
36514         return this.lookup[id];
36515     },
36516
36517     
36518     /**
36519      * Returns the column for a specified dataIndex.
36520      * @param {String} dataIndex The column dataIndex
36521      * @return {Object|Boolean} the column or false if not found
36522      */
36523     getColumnByDataIndex: function(dataIndex){
36524         var index = this.findColumnIndex(dataIndex);
36525         return index > -1 ? this.config[index] : false;
36526     },
36527     
36528     /**
36529      * Returns the index for a specified column id.
36530      * @param {String} id The column id
36531      * @return {Number} the index, or -1 if not found
36532      */
36533     getIndexById : function(id){
36534         for(var i = 0, len = this.config.length; i < len; i++){
36535             if(this.config[i].id == id){
36536                 return i;
36537             }
36538         }
36539         return -1;
36540     },
36541     
36542     /**
36543      * Returns the index for a specified column dataIndex.
36544      * @param {String} dataIndex The column dataIndex
36545      * @return {Number} the index, or -1 if not found
36546      */
36547     
36548     findColumnIndex : function(dataIndex){
36549         for(var i = 0, len = this.config.length; i < len; i++){
36550             if(this.config[i].dataIndex == dataIndex){
36551                 return i;
36552             }
36553         }
36554         return -1;
36555     },
36556     
36557     
36558     moveColumn : function(oldIndex, newIndex){
36559         var c = this.config[oldIndex];
36560         this.config.splice(oldIndex, 1);
36561         this.config.splice(newIndex, 0, c);
36562         this.dataMap = null;
36563         this.fireEvent("columnmoved", this, oldIndex, newIndex);
36564     },
36565
36566     isLocked : function(colIndex){
36567         return this.config[colIndex].locked === true;
36568     },
36569
36570     setLocked : function(colIndex, value, suppressEvent){
36571         if(this.isLocked(colIndex) == value){
36572             return;
36573         }
36574         this.config[colIndex].locked = value;
36575         if(!suppressEvent){
36576             this.fireEvent("columnlockchange", this, colIndex, value);
36577         }
36578     },
36579
36580     getTotalLockedWidth : function(){
36581         var totalWidth = 0;
36582         for(var i = 0; i < this.config.length; i++){
36583             if(this.isLocked(i) && !this.isHidden(i)){
36584                 this.totalWidth += this.getColumnWidth(i);
36585             }
36586         }
36587         return totalWidth;
36588     },
36589
36590     getLockedCount : function(){
36591         for(var i = 0, len = this.config.length; i < len; i++){
36592             if(!this.isLocked(i)){
36593                 return i;
36594             }
36595         }
36596     },
36597
36598     /**
36599      * Returns the number of columns.
36600      * @return {Number}
36601      */
36602     getColumnCount : function(visibleOnly){
36603         if(visibleOnly === true){
36604             var c = 0;
36605             for(var i = 0, len = this.config.length; i < len; i++){
36606                 if(!this.isHidden(i)){
36607                     c++;
36608                 }
36609             }
36610             return c;
36611         }
36612         return this.config.length;
36613     },
36614
36615     /**
36616      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
36617      * @param {Function} fn
36618      * @param {Object} scope (optional)
36619      * @return {Array} result
36620      */
36621     getColumnsBy : function(fn, scope){
36622         var r = [];
36623         for(var i = 0, len = this.config.length; i < len; i++){
36624             var c = this.config[i];
36625             if(fn.call(scope||this, c, i) === true){
36626                 r[r.length] = c;
36627             }
36628         }
36629         return r;
36630     },
36631
36632     /**
36633      * Returns true if the specified column is sortable.
36634      * @param {Number} col The column index
36635      * @return {Boolean}
36636      */
36637     isSortable : function(col){
36638         if(typeof this.config[col].sortable == "undefined"){
36639             return this.defaultSortable;
36640         }
36641         return this.config[col].sortable;
36642     },
36643
36644     /**
36645      * Returns the rendering (formatting) function defined for the column.
36646      * @param {Number} col The column index.
36647      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
36648      */
36649     getRenderer : function(col){
36650         if(!this.config[col].renderer){
36651             return Roo.grid.ColumnModel.defaultRenderer;
36652         }
36653         return this.config[col].renderer;
36654     },
36655
36656     /**
36657      * Sets the rendering (formatting) function for a column.
36658      * @param {Number} col The column index
36659      * @param {Function} fn The function to use to process the cell's raw data
36660      * to return HTML markup for the grid view. The render function is called with
36661      * the following parameters:<ul>
36662      * <li>Data value.</li>
36663      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
36664      * <li>css A CSS style string to apply to the table cell.</li>
36665      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
36666      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
36667      * <li>Row index</li>
36668      * <li>Column index</li>
36669      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
36670      */
36671     setRenderer : function(col, fn){
36672         this.config[col].renderer = fn;
36673     },
36674
36675     /**
36676      * Returns the width for the specified column.
36677      * @param {Number} col The column index
36678      * @return {Number}
36679      */
36680     getColumnWidth : function(col){
36681         return this.config[col].width * 1 || this.defaultWidth;
36682     },
36683
36684     /**
36685      * Sets the width for a column.
36686      * @param {Number} col The column index
36687      * @param {Number} width The new width
36688      */
36689     setColumnWidth : function(col, width, suppressEvent){
36690         this.config[col].width = width;
36691         this.totalWidth = null;
36692         if(!suppressEvent){
36693              this.fireEvent("widthchange", this, col, width);
36694         }
36695     },
36696
36697     /**
36698      * Returns the total width of all columns.
36699      * @param {Boolean} includeHidden True to include hidden column widths
36700      * @return {Number}
36701      */
36702     getTotalWidth : function(includeHidden){
36703         if(!this.totalWidth){
36704             this.totalWidth = 0;
36705             for(var i = 0, len = this.config.length; i < len; i++){
36706                 if(includeHidden || !this.isHidden(i)){
36707                     this.totalWidth += this.getColumnWidth(i);
36708                 }
36709             }
36710         }
36711         return this.totalWidth;
36712     },
36713
36714     /**
36715      * Returns the header for the specified column.
36716      * @param {Number} col The column index
36717      * @return {String}
36718      */
36719     getColumnHeader : function(col){
36720         return this.config[col].header;
36721     },
36722
36723     /**
36724      * Sets the header for a column.
36725      * @param {Number} col The column index
36726      * @param {String} header The new header
36727      */
36728     setColumnHeader : function(col, header){
36729         this.config[col].header = header;
36730         this.fireEvent("headerchange", this, col, header);
36731     },
36732
36733     /**
36734      * Returns the tooltip for the specified column.
36735      * @param {Number} col The column index
36736      * @return {String}
36737      */
36738     getColumnTooltip : function(col){
36739             return this.config[col].tooltip;
36740     },
36741     /**
36742      * Sets the tooltip for a column.
36743      * @param {Number} col The column index
36744      * @param {String} tooltip The new tooltip
36745      */
36746     setColumnTooltip : function(col, tooltip){
36747             this.config[col].tooltip = tooltip;
36748     },
36749
36750     /**
36751      * Returns the dataIndex for the specified column.
36752      * @param {Number} col The column index
36753      * @return {Number}
36754      */
36755     getDataIndex : function(col){
36756         return this.config[col].dataIndex;
36757     },
36758
36759     /**
36760      * Sets the dataIndex for a column.
36761      * @param {Number} col The column index
36762      * @param {Number} dataIndex The new dataIndex
36763      */
36764     setDataIndex : function(col, dataIndex){
36765         this.config[col].dataIndex = dataIndex;
36766     },
36767
36768     
36769     
36770     /**
36771      * Returns true if the cell is editable.
36772      * @param {Number} colIndex The column index
36773      * @param {Number} rowIndex The row index
36774      * @return {Boolean}
36775      */
36776     isCellEditable : function(colIndex, rowIndex){
36777         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
36778     },
36779
36780     /**
36781      * Returns the editor defined for the cell/column.
36782      * return false or null to disable editing.
36783      * @param {Number} colIndex The column index
36784      * @param {Number} rowIndex The row index
36785      * @return {Object}
36786      */
36787     getCellEditor : function(colIndex, rowIndex){
36788         return this.config[colIndex].editor;
36789     },
36790
36791     /**
36792      * Sets if a column is editable.
36793      * @param {Number} col The column index
36794      * @param {Boolean} editable True if the column is editable
36795      */
36796     setEditable : function(col, editable){
36797         this.config[col].editable = editable;
36798     },
36799
36800
36801     /**
36802      * Returns true if the column is hidden.
36803      * @param {Number} colIndex The column index
36804      * @return {Boolean}
36805      */
36806     isHidden : function(colIndex){
36807         return this.config[colIndex].hidden;
36808     },
36809
36810
36811     /**
36812      * Returns true if the column width cannot be changed
36813      */
36814     isFixed : function(colIndex){
36815         return this.config[colIndex].fixed;
36816     },
36817
36818     /**
36819      * Returns true if the column can be resized
36820      * @return {Boolean}
36821      */
36822     isResizable : function(colIndex){
36823         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
36824     },
36825     /**
36826      * Sets if a column is hidden.
36827      * @param {Number} colIndex The column index
36828      * @param {Boolean} hidden True if the column is hidden
36829      */
36830     setHidden : function(colIndex, hidden){
36831         this.config[colIndex].hidden = hidden;
36832         this.totalWidth = null;
36833         this.fireEvent("hiddenchange", this, colIndex, hidden);
36834     },
36835
36836     /**
36837      * Sets the editor for a column.
36838      * @param {Number} col The column index
36839      * @param {Object} editor The editor object
36840      */
36841     setEditor : function(col, editor){
36842         this.config[col].editor = editor;
36843     }
36844 });
36845
36846 Roo.grid.ColumnModel.defaultRenderer = function(value){
36847         if(typeof value == "string" && value.length < 1){
36848             return "&#160;";
36849         }
36850         return value;
36851 };
36852
36853 // Alias for backwards compatibility
36854 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
36855 /*
36856  * Based on:
36857  * Ext JS Library 1.1.1
36858  * Copyright(c) 2006-2007, Ext JS, LLC.
36859  *
36860  * Originally Released Under LGPL - original licence link has changed is not relivant.
36861  *
36862  * Fork - LGPL
36863  * <script type="text/javascript">
36864  */
36865
36866 /**
36867  * @class Roo.grid.AbstractSelectionModel
36868  * @extends Roo.util.Observable
36869  * Abstract base class for grid SelectionModels.  It provides the interface that should be
36870  * implemented by descendant classes.  This class should not be directly instantiated.
36871  * @constructor
36872  */
36873 Roo.grid.AbstractSelectionModel = function(){
36874     this.locked = false;
36875     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
36876 };
36877
36878 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
36879     /** @ignore Called by the grid automatically. Do not call directly. */
36880     init : function(grid){
36881         this.grid = grid;
36882         this.initEvents();
36883     },
36884
36885     /**
36886      * Locks the selections.
36887      */
36888     lock : function(){
36889         this.locked = true;
36890     },
36891
36892     /**
36893      * Unlocks the selections.
36894      */
36895     unlock : function(){
36896         this.locked = false;
36897     },
36898
36899     /**
36900      * Returns true if the selections are locked.
36901      * @return {Boolean}
36902      */
36903     isLocked : function(){
36904         return this.locked;
36905     }
36906 });/*
36907  * Based on:
36908  * Ext JS Library 1.1.1
36909  * Copyright(c) 2006-2007, Ext JS, LLC.
36910  *
36911  * Originally Released Under LGPL - original licence link has changed is not relivant.
36912  *
36913  * Fork - LGPL
36914  * <script type="text/javascript">
36915  */
36916 /**
36917  * @extends Roo.grid.AbstractSelectionModel
36918  * @class Roo.grid.RowSelectionModel
36919  * The default SelectionModel used by {@link Roo.grid.Grid}.
36920  * It supports multiple selections and keyboard selection/navigation. 
36921  * @constructor
36922  * @param {Object} config
36923  */
36924 Roo.grid.RowSelectionModel = function(config){
36925     Roo.apply(this, config);
36926     this.selections = new Roo.util.MixedCollection(false, function(o){
36927         return o.id;
36928     });
36929
36930     this.last = false;
36931     this.lastActive = false;
36932
36933     this.addEvents({
36934         /**
36935              * @event selectionchange
36936              * Fires when the selection changes
36937              * @param {SelectionModel} this
36938              */
36939             "selectionchange" : true,
36940         /**
36941              * @event afterselectionchange
36942              * Fires after the selection changes (eg. by key press or clicking)
36943              * @param {SelectionModel} this
36944              */
36945             "afterselectionchange" : true,
36946         /**
36947              * @event beforerowselect
36948              * Fires when a row is selected being selected, return false to cancel.
36949              * @param {SelectionModel} this
36950              * @param {Number} rowIndex The selected index
36951              * @param {Boolean} keepExisting False if other selections will be cleared
36952              */
36953             "beforerowselect" : true,
36954         /**
36955              * @event rowselect
36956              * Fires when a row is selected.
36957              * @param {SelectionModel} this
36958              * @param {Number} rowIndex The selected index
36959              * @param {Roo.data.Record} r The record
36960              */
36961             "rowselect" : true,
36962         /**
36963              * @event rowdeselect
36964              * Fires when a row is deselected.
36965              * @param {SelectionModel} this
36966              * @param {Number} rowIndex The selected index
36967              */
36968         "rowdeselect" : true
36969     });
36970     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
36971     this.locked = false;
36972 };
36973
36974 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
36975     /**
36976      * @cfg {Boolean} singleSelect
36977      * True to allow selection of only one row at a time (defaults to false)
36978      */
36979     singleSelect : false,
36980
36981     // private
36982     initEvents : function(){
36983
36984         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
36985             this.grid.on("mousedown", this.handleMouseDown, this);
36986         }else{ // allow click to work like normal
36987             this.grid.on("rowclick", this.handleDragableRowClick, this);
36988         }
36989
36990         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
36991             "up" : function(e){
36992                 if(!e.shiftKey){
36993                     this.selectPrevious(e.shiftKey);
36994                 }else if(this.last !== false && this.lastActive !== false){
36995                     var last = this.last;
36996                     this.selectRange(this.last,  this.lastActive-1);
36997                     this.grid.getView().focusRow(this.lastActive);
36998                     if(last !== false){
36999                         this.last = last;
37000                     }
37001                 }else{
37002                     this.selectFirstRow();
37003                 }
37004                 this.fireEvent("afterselectionchange", this);
37005             },
37006             "down" : function(e){
37007                 if(!e.shiftKey){
37008                     this.selectNext(e.shiftKey);
37009                 }else if(this.last !== false && this.lastActive !== false){
37010                     var last = this.last;
37011                     this.selectRange(this.last,  this.lastActive+1);
37012                     this.grid.getView().focusRow(this.lastActive);
37013                     if(last !== false){
37014                         this.last = last;
37015                     }
37016                 }else{
37017                     this.selectFirstRow();
37018                 }
37019                 this.fireEvent("afterselectionchange", this);
37020             },
37021             scope: this
37022         });
37023
37024         var view = this.grid.view;
37025         view.on("refresh", this.onRefresh, this);
37026         view.on("rowupdated", this.onRowUpdated, this);
37027         view.on("rowremoved", this.onRemove, this);
37028     },
37029
37030     // private
37031     onRefresh : function(){
37032         var ds = this.grid.dataSource, i, v = this.grid.view;
37033         var s = this.selections;
37034         s.each(function(r){
37035             if((i = ds.indexOfId(r.id)) != -1){
37036                 v.onRowSelect(i);
37037             }else{
37038                 s.remove(r);
37039             }
37040         });
37041     },
37042
37043     // private
37044     onRemove : function(v, index, r){
37045         this.selections.remove(r);
37046     },
37047
37048     // private
37049     onRowUpdated : function(v, index, r){
37050         if(this.isSelected(r)){
37051             v.onRowSelect(index);
37052         }
37053     },
37054
37055     /**
37056      * Select records.
37057      * @param {Array} records The records to select
37058      * @param {Boolean} keepExisting (optional) True to keep existing selections
37059      */
37060     selectRecords : function(records, keepExisting){
37061         if(!keepExisting){
37062             this.clearSelections();
37063         }
37064         var ds = this.grid.dataSource;
37065         for(var i = 0, len = records.length; i < len; i++){
37066             this.selectRow(ds.indexOf(records[i]), true);
37067         }
37068     },
37069
37070     /**
37071      * Gets the number of selected rows.
37072      * @return {Number}
37073      */
37074     getCount : function(){
37075         return this.selections.length;
37076     },
37077
37078     /**
37079      * Selects the first row in the grid.
37080      */
37081     selectFirstRow : function(){
37082         this.selectRow(0);
37083     },
37084
37085     /**
37086      * Select the last row.
37087      * @param {Boolean} keepExisting (optional) True to keep existing selections
37088      */
37089     selectLastRow : function(keepExisting){
37090         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
37091     },
37092
37093     /**
37094      * Selects the row immediately following the last selected row.
37095      * @param {Boolean} keepExisting (optional) True to keep existing selections
37096      */
37097     selectNext : function(keepExisting){
37098         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
37099             this.selectRow(this.last+1, keepExisting);
37100             this.grid.getView().focusRow(this.last);
37101         }
37102     },
37103
37104     /**
37105      * Selects the row that precedes the last selected row.
37106      * @param {Boolean} keepExisting (optional) True to keep existing selections
37107      */
37108     selectPrevious : function(keepExisting){
37109         if(this.last){
37110             this.selectRow(this.last-1, keepExisting);
37111             this.grid.getView().focusRow(this.last);
37112         }
37113     },
37114
37115     /**
37116      * Returns the selected records
37117      * @return {Array} Array of selected records
37118      */
37119     getSelections : function(){
37120         return [].concat(this.selections.items);
37121     },
37122
37123     /**
37124      * Returns the first selected record.
37125      * @return {Record}
37126      */
37127     getSelected : function(){
37128         return this.selections.itemAt(0);
37129     },
37130
37131
37132     /**
37133      * Clears all selections.
37134      */
37135     clearSelections : function(fast){
37136         if(this.locked) return;
37137         if(fast !== true){
37138             var ds = this.grid.dataSource;
37139             var s = this.selections;
37140             s.each(function(r){
37141                 this.deselectRow(ds.indexOfId(r.id));
37142             }, this);
37143             s.clear();
37144         }else{
37145             this.selections.clear();
37146         }
37147         this.last = false;
37148     },
37149
37150
37151     /**
37152      * Selects all rows.
37153      */
37154     selectAll : function(){
37155         if(this.locked) return;
37156         this.selections.clear();
37157         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
37158             this.selectRow(i, true);
37159         }
37160     },
37161
37162     /**
37163      * Returns True if there is a selection.
37164      * @return {Boolean}
37165      */
37166     hasSelection : function(){
37167         return this.selections.length > 0;
37168     },
37169
37170     /**
37171      * Returns True if the specified row is selected.
37172      * @param {Number/Record} record The record or index of the record to check
37173      * @return {Boolean}
37174      */
37175     isSelected : function(index){
37176         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
37177         return (r && this.selections.key(r.id) ? true : false);
37178     },
37179
37180     /**
37181      * Returns True if the specified record id is selected.
37182      * @param {String} id The id of record to check
37183      * @return {Boolean}
37184      */
37185     isIdSelected : function(id){
37186         return (this.selections.key(id) ? true : false);
37187     },
37188
37189     // private
37190     handleMouseDown : function(e, t){
37191         var view = this.grid.getView(), rowIndex;
37192         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
37193             return;
37194         };
37195         if(e.shiftKey && this.last !== false){
37196             var last = this.last;
37197             this.selectRange(last, rowIndex, e.ctrlKey);
37198             this.last = last; // reset the last
37199             view.focusRow(rowIndex);
37200         }else{
37201             var isSelected = this.isSelected(rowIndex);
37202             if(e.button !== 0 && isSelected){
37203                 view.focusRow(rowIndex);
37204             }else if(e.ctrlKey && isSelected){
37205                 this.deselectRow(rowIndex);
37206             }else if(!isSelected){
37207                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
37208                 view.focusRow(rowIndex);
37209             }
37210         }
37211         this.fireEvent("afterselectionchange", this);
37212     },
37213     // private
37214     handleDragableRowClick :  function(grid, rowIndex, e) 
37215     {
37216         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
37217             this.selectRow(rowIndex, false);
37218             grid.view.focusRow(rowIndex);
37219              this.fireEvent("afterselectionchange", this);
37220         }
37221     },
37222     
37223     /**
37224      * Selects multiple rows.
37225      * @param {Array} rows Array of the indexes of the row to select
37226      * @param {Boolean} keepExisting (optional) True to keep existing selections
37227      */
37228     selectRows : function(rows, keepExisting){
37229         if(!keepExisting){
37230             this.clearSelections();
37231         }
37232         for(var i = 0, len = rows.length; i < len; i++){
37233             this.selectRow(rows[i], true);
37234         }
37235     },
37236
37237     /**
37238      * Selects a range of rows. All rows in between startRow and endRow are also selected.
37239      * @param {Number} startRow The index of the first row in the range
37240      * @param {Number} endRow The index of the last row in the range
37241      * @param {Boolean} keepExisting (optional) True to retain existing selections
37242      */
37243     selectRange : function(startRow, endRow, keepExisting){
37244         if(this.locked) return;
37245         if(!keepExisting){
37246             this.clearSelections();
37247         }
37248         if(startRow <= endRow){
37249             for(var i = startRow; i <= endRow; i++){
37250                 this.selectRow(i, true);
37251             }
37252         }else{
37253             for(var i = startRow; i >= endRow; i--){
37254                 this.selectRow(i, true);
37255             }
37256         }
37257     },
37258
37259     /**
37260      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
37261      * @param {Number} startRow The index of the first row in the range
37262      * @param {Number} endRow The index of the last row in the range
37263      */
37264     deselectRange : function(startRow, endRow, preventViewNotify){
37265         if(this.locked) return;
37266         for(var i = startRow; i <= endRow; i++){
37267             this.deselectRow(i, preventViewNotify);
37268         }
37269     },
37270
37271     /**
37272      * Selects a row.
37273      * @param {Number} row The index of the row to select
37274      * @param {Boolean} keepExisting (optional) True to keep existing selections
37275      */
37276     selectRow : function(index, keepExisting, preventViewNotify){
37277         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
37278         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
37279             if(!keepExisting || this.singleSelect){
37280                 this.clearSelections();
37281             }
37282             var r = this.grid.dataSource.getAt(index);
37283             this.selections.add(r);
37284             this.last = this.lastActive = index;
37285             if(!preventViewNotify){
37286                 this.grid.getView().onRowSelect(index);
37287             }
37288             this.fireEvent("rowselect", this, index, r);
37289             this.fireEvent("selectionchange", this);
37290         }
37291     },
37292
37293     /**
37294      * Deselects a row.
37295      * @param {Number} row The index of the row to deselect
37296      */
37297     deselectRow : function(index, preventViewNotify){
37298         if(this.locked) return;
37299         if(this.last == index){
37300             this.last = false;
37301         }
37302         if(this.lastActive == index){
37303             this.lastActive = false;
37304         }
37305         var r = this.grid.dataSource.getAt(index);
37306         this.selections.remove(r);
37307         if(!preventViewNotify){
37308             this.grid.getView().onRowDeselect(index);
37309         }
37310         this.fireEvent("rowdeselect", this, index);
37311         this.fireEvent("selectionchange", this);
37312     },
37313
37314     // private
37315     restoreLast : function(){
37316         if(this._last){
37317             this.last = this._last;
37318         }
37319     },
37320
37321     // private
37322     acceptsNav : function(row, col, cm){
37323         return !cm.isHidden(col) && cm.isCellEditable(col, row);
37324     },
37325
37326     // private
37327     onEditorKey : function(field, e){
37328         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
37329         if(k == e.TAB){
37330             e.stopEvent();
37331             ed.completeEdit();
37332             if(e.shiftKey){
37333                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
37334             }else{
37335                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37336             }
37337         }else if(k == e.ENTER && !e.ctrlKey){
37338             e.stopEvent();
37339             ed.completeEdit();
37340             if(e.shiftKey){
37341                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
37342             }else{
37343                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
37344             }
37345         }else if(k == e.ESC){
37346             ed.cancelEdit();
37347         }
37348         if(newCell){
37349             g.startEditing(newCell[0], newCell[1]);
37350         }
37351     }
37352 });/*
37353  * Based on:
37354  * Ext JS Library 1.1.1
37355  * Copyright(c) 2006-2007, Ext JS, LLC.
37356  *
37357  * Originally Released Under LGPL - original licence link has changed is not relivant.
37358  *
37359  * Fork - LGPL
37360  * <script type="text/javascript">
37361  */
37362 /**
37363  * @class Roo.grid.CellSelectionModel
37364  * @extends Roo.grid.AbstractSelectionModel
37365  * This class provides the basic implementation for cell selection in a grid.
37366  * @constructor
37367  * @param {Object} config The object containing the configuration of this model.
37368  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
37369  */
37370 Roo.grid.CellSelectionModel = function(config){
37371     Roo.apply(this, config);
37372
37373     this.selection = null;
37374
37375     this.addEvents({
37376         /**
37377              * @event beforerowselect
37378              * Fires before a cell is selected.
37379              * @param {SelectionModel} this
37380              * @param {Number} rowIndex The selected row index
37381              * @param {Number} colIndex The selected cell index
37382              */
37383             "beforecellselect" : true,
37384         /**
37385              * @event cellselect
37386              * Fires when a cell is selected.
37387              * @param {SelectionModel} this
37388              * @param {Number} rowIndex The selected row index
37389              * @param {Number} colIndex The selected cell index
37390              */
37391             "cellselect" : true,
37392         /**
37393              * @event selectionchange
37394              * Fires when the active selection changes.
37395              * @param {SelectionModel} this
37396              * @param {Object} selection null for no selection or an object (o) with two properties
37397                 <ul>
37398                 <li>o.record: the record object for the row the selection is in</li>
37399                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
37400                 </ul>
37401              */
37402             "selectionchange" : true,
37403         /**
37404              * @event tabend
37405              * Fires when the tab (or enter) was pressed on the last editable cell
37406              * You can use this to trigger add new row.
37407              * @param {SelectionModel} this
37408              */
37409             "tabend" : true,
37410          /**
37411              * @event beforeeditnext
37412              * Fires before the next editable sell is made active
37413              * You can use this to skip to another cell or fire the tabend
37414              *    if you set cell to false
37415              * @param {Object} eventdata object : { cell : [ row, col ] } 
37416              */
37417             "beforeeditnext" : true
37418     });
37419     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
37420 };
37421
37422 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
37423     
37424     enter_is_tab: false,
37425
37426     /** @ignore */
37427     initEvents : function(){
37428         this.grid.on("mousedown", this.handleMouseDown, this);
37429         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
37430         var view = this.grid.view;
37431         view.on("refresh", this.onViewChange, this);
37432         view.on("rowupdated", this.onRowUpdated, this);
37433         view.on("beforerowremoved", this.clearSelections, this);
37434         view.on("beforerowsinserted", this.clearSelections, this);
37435         if(this.grid.isEditor){
37436             this.grid.on("beforeedit", this.beforeEdit,  this);
37437         }
37438     },
37439
37440         //private
37441     beforeEdit : function(e){
37442         this.select(e.row, e.column, false, true, e.record);
37443     },
37444
37445         //private
37446     onRowUpdated : function(v, index, r){
37447         if(this.selection && this.selection.record == r){
37448             v.onCellSelect(index, this.selection.cell[1]);
37449         }
37450     },
37451
37452         //private
37453     onViewChange : function(){
37454         this.clearSelections(true);
37455     },
37456
37457         /**
37458          * Returns the currently selected cell,.
37459          * @return {Array} The selected cell (row, column) or null if none selected.
37460          */
37461     getSelectedCell : function(){
37462         return this.selection ? this.selection.cell : null;
37463     },
37464
37465     /**
37466      * Clears all selections.
37467      * @param {Boolean} true to prevent the gridview from being notified about the change.
37468      */
37469     clearSelections : function(preventNotify){
37470         var s = this.selection;
37471         if(s){
37472             if(preventNotify !== true){
37473                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
37474             }
37475             this.selection = null;
37476             this.fireEvent("selectionchange", this, null);
37477         }
37478     },
37479
37480     /**
37481      * Returns true if there is a selection.
37482      * @return {Boolean}
37483      */
37484     hasSelection : function(){
37485         return this.selection ? true : false;
37486     },
37487
37488     /** @ignore */
37489     handleMouseDown : function(e, t){
37490         var v = this.grid.getView();
37491         if(this.isLocked()){
37492             return;
37493         };
37494         var row = v.findRowIndex(t);
37495         var cell = v.findCellIndex(t);
37496         if(row !== false && cell !== false){
37497             this.select(row, cell);
37498         }
37499     },
37500
37501     /**
37502      * Selects a cell.
37503      * @param {Number} rowIndex
37504      * @param {Number} collIndex
37505      */
37506     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
37507         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
37508             this.clearSelections();
37509             r = r || this.grid.dataSource.getAt(rowIndex);
37510             this.selection = {
37511                 record : r,
37512                 cell : [rowIndex, colIndex]
37513             };
37514             if(!preventViewNotify){
37515                 var v = this.grid.getView();
37516                 v.onCellSelect(rowIndex, colIndex);
37517                 if(preventFocus !== true){
37518                     v.focusCell(rowIndex, colIndex);
37519                 }
37520             }
37521             this.fireEvent("cellselect", this, rowIndex, colIndex);
37522             this.fireEvent("selectionchange", this, this.selection);
37523         }
37524     },
37525
37526         //private
37527     isSelectable : function(rowIndex, colIndex, cm){
37528         return !cm.isHidden(colIndex);
37529     },
37530
37531     /** @ignore */
37532     handleKeyDown : function(e){
37533         //Roo.log('Cell Sel Model handleKeyDown');
37534         if(!e.isNavKeyPress()){
37535             return;
37536         }
37537         var g = this.grid, s = this.selection;
37538         if(!s){
37539             e.stopEvent();
37540             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
37541             if(cell){
37542                 this.select(cell[0], cell[1]);
37543             }
37544             return;
37545         }
37546         var sm = this;
37547         var walk = function(row, col, step){
37548             return g.walkCells(row, col, step, sm.isSelectable,  sm);
37549         };
37550         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
37551         var newCell;
37552
37553       
37554
37555         switch(k){
37556             case e.TAB:
37557                 // handled by onEditorKey
37558                 if (g.isEditor && g.editing) {
37559                     return;
37560                 }
37561                 if(e.shiftKey) {
37562                     newCell = walk(r, c-1, -1);
37563                 } else {
37564                     newCell = walk(r, c+1, 1);
37565                 }
37566                 break;
37567             
37568             case e.DOWN:
37569                newCell = walk(r+1, c, 1);
37570                 break;
37571             
37572             case e.UP:
37573                 newCell = walk(r-1, c, -1);
37574                 break;
37575             
37576             case e.RIGHT:
37577                 newCell = walk(r, c+1, 1);
37578                 break;
37579             
37580             case e.LEFT:
37581                 newCell = walk(r, c-1, -1);
37582                 break;
37583             
37584             case e.ENTER:
37585                 
37586                 if(g.isEditor && !g.editing){
37587                    g.startEditing(r, c);
37588                    e.stopEvent();
37589                    return;
37590                 }
37591                 
37592                 
37593              break;
37594         };
37595         if(newCell){
37596             this.select(newCell[0], newCell[1]);
37597             e.stopEvent();
37598             
37599         }
37600     },
37601
37602     acceptsNav : function(row, col, cm){
37603         return !cm.isHidden(col) && cm.isCellEditable(col, row);
37604     },
37605     /**
37606      * Selects a cell.
37607      * @param {Number} field (not used) - as it's normally used as a listener
37608      * @param {Number} e - event - fake it by using
37609      *
37610      * var e = Roo.EventObjectImpl.prototype;
37611      * e.keyCode = e.TAB
37612      *
37613      * 
37614      */
37615     onEditorKey : function(field, e){
37616         
37617         var k = e.getKey(),
37618             newCell,
37619             g = this.grid,
37620             ed = g.activeEditor,
37621             forward = false;
37622         ///Roo.log('onEditorKey' + k);
37623         
37624         
37625         if (this.enter_is_tab && k == e.ENTER) {
37626             k = e.TAB;
37627         }
37628         
37629         if(k == e.TAB){
37630             if(e.shiftKey){
37631                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
37632             }else{
37633                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37634                 forward = true;
37635             }
37636             
37637             e.stopEvent();
37638             
37639         } else if(k == e.ENTER &&  !e.ctrlKey){
37640             ed.completeEdit();
37641             e.stopEvent();
37642             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37643         
37644                 } else if(k == e.ESC){
37645             ed.cancelEdit();
37646         }
37647                 
37648         if (newCell) {
37649             var ecall = { cell : newCell, forward : forward };
37650             this.fireEvent('beforeeditnext', ecall );
37651             newCell = ecall.cell;
37652                         forward = ecall.forward;
37653         }
37654                 
37655         if(newCell){
37656             //Roo.log('next cell after edit');
37657             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
37658         } else if (forward) {
37659             // tabbed past last
37660             this.fireEvent.defer(100, this, ['tabend',this]);
37661         }
37662     }
37663 });/*
37664  * Based on:
37665  * Ext JS Library 1.1.1
37666  * Copyright(c) 2006-2007, Ext JS, LLC.
37667  *
37668  * Originally Released Under LGPL - original licence link has changed is not relivant.
37669  *
37670  * Fork - LGPL
37671  * <script type="text/javascript">
37672  */
37673  
37674 /**
37675  * @class Roo.grid.EditorGrid
37676  * @extends Roo.grid.Grid
37677  * Class for creating and editable grid.
37678  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
37679  * The container MUST have some type of size defined for the grid to fill. The container will be 
37680  * automatically set to position relative if it isn't already.
37681  * @param {Object} dataSource The data model to bind to
37682  * @param {Object} colModel The column model with info about this grid's columns
37683  */
37684 Roo.grid.EditorGrid = function(container, config){
37685     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
37686     this.getGridEl().addClass("xedit-grid");
37687
37688     if(!this.selModel){
37689         this.selModel = new Roo.grid.CellSelectionModel();
37690     }
37691
37692     this.activeEditor = null;
37693
37694         this.addEvents({
37695             /**
37696              * @event beforeedit
37697              * Fires before cell editing is triggered. The edit event object has the following properties <br />
37698              * <ul style="padding:5px;padding-left:16px;">
37699              * <li>grid - This grid</li>
37700              * <li>record - The record being edited</li>
37701              * <li>field - The field name being edited</li>
37702              * <li>value - The value for the field being edited.</li>
37703              * <li>row - The grid row index</li>
37704              * <li>column - The grid column index</li>
37705              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37706              * </ul>
37707              * @param {Object} e An edit event (see above for description)
37708              */
37709             "beforeedit" : true,
37710             /**
37711              * @event afteredit
37712              * Fires after a cell is edited. <br />
37713              * <ul style="padding:5px;padding-left:16px;">
37714              * <li>grid - This grid</li>
37715              * <li>record - The record being edited</li>
37716              * <li>field - The field name being edited</li>
37717              * <li>value - The value being set</li>
37718              * <li>originalValue - The original value for the field, before the edit.</li>
37719              * <li>row - The grid row index</li>
37720              * <li>column - The grid column index</li>
37721              * </ul>
37722              * @param {Object} e An edit event (see above for description)
37723              */
37724             "afteredit" : true,
37725             /**
37726              * @event validateedit
37727              * Fires after a cell is edited, but before the value is set in the record. 
37728          * You can use this to modify the value being set in the field, Return false
37729              * to cancel the change. The edit event object has the following properties <br />
37730              * <ul style="padding:5px;padding-left:16px;">
37731          * <li>editor - This editor</li>
37732              * <li>grid - This grid</li>
37733              * <li>record - The record being edited</li>
37734              * <li>field - The field name being edited</li>
37735              * <li>value - The value being set</li>
37736              * <li>originalValue - The original value for the field, before the edit.</li>
37737              * <li>row - The grid row index</li>
37738              * <li>column - The grid column index</li>
37739              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37740              * </ul>
37741              * @param {Object} e An edit event (see above for description)
37742              */
37743             "validateedit" : true
37744         });
37745     this.on("bodyscroll", this.stopEditing,  this);
37746     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
37747 };
37748
37749 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
37750     /**
37751      * @cfg {Number} clicksToEdit
37752      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
37753      */
37754     clicksToEdit: 2,
37755
37756     // private
37757     isEditor : true,
37758     // private
37759     trackMouseOver: false, // causes very odd FF errors
37760
37761     onCellDblClick : function(g, row, col){
37762         this.startEditing(row, col);
37763     },
37764
37765     onEditComplete : function(ed, value, startValue){
37766         this.editing = false;
37767         this.activeEditor = null;
37768         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
37769         var r = ed.record;
37770         var field = this.colModel.getDataIndex(ed.col);
37771         var e = {
37772             grid: this,
37773             record: r,
37774             field: field,
37775             originalValue: startValue,
37776             value: value,
37777             row: ed.row,
37778             column: ed.col,
37779             cancel:false,
37780             editor: ed
37781         };
37782         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
37783         cell.show();
37784           
37785         if(String(value) !== String(startValue)){
37786             
37787             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
37788                 r.set(field, e.value);
37789                 // if we are dealing with a combo box..
37790                 // then we also set the 'name' colum to be the displayField
37791                 if (ed.field.displayField && ed.field.name) {
37792                     r.set(ed.field.name, ed.field.el.dom.value);
37793                 }
37794                 
37795                 delete e.cancel; //?? why!!!
37796                 this.fireEvent("afteredit", e);
37797             }
37798         } else {
37799             this.fireEvent("afteredit", e); // always fire it!
37800         }
37801         this.view.focusCell(ed.row, ed.col);
37802     },
37803
37804     /**
37805      * Starts editing the specified for the specified row/column
37806      * @param {Number} rowIndex
37807      * @param {Number} colIndex
37808      */
37809     startEditing : function(row, col){
37810         this.stopEditing();
37811         if(this.colModel.isCellEditable(col, row)){
37812             this.view.ensureVisible(row, col, true);
37813           
37814             var r = this.dataSource.getAt(row);
37815             var field = this.colModel.getDataIndex(col);
37816             var cell = Roo.get(this.view.getCell(row,col));
37817             var e = {
37818                 grid: this,
37819                 record: r,
37820                 field: field,
37821                 value: r.data[field],
37822                 row: row,
37823                 column: col,
37824                 cancel:false 
37825             };
37826             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
37827                 this.editing = true;
37828                 var ed = this.colModel.getCellEditor(col, row);
37829                 
37830                 if (!ed) {
37831                     return;
37832                 }
37833                 if(!ed.rendered){
37834                     ed.render(ed.parentEl || document.body);
37835                 }
37836                 ed.field.reset();
37837                
37838                 cell.hide();
37839                 
37840                 (function(){ // complex but required for focus issues in safari, ie and opera
37841                     ed.row = row;
37842                     ed.col = col;
37843                     ed.record = r;
37844                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
37845                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
37846                     this.activeEditor = ed;
37847                     var v = r.data[field];
37848                     ed.startEdit(this.view.getCell(row, col), v);
37849                     // combo's with 'displayField and name set
37850                     if (ed.field.displayField && ed.field.name) {
37851                         ed.field.el.dom.value = r.data[ed.field.name];
37852                     }
37853                     
37854                     
37855                 }).defer(50, this);
37856             }
37857         }
37858     },
37859         
37860     /**
37861      * Stops any active editing
37862      */
37863     stopEditing : function(){
37864         if(this.activeEditor){
37865             this.activeEditor.completeEdit();
37866         }
37867         this.activeEditor = null;
37868     }
37869 });/*
37870  * Based on:
37871  * Ext JS Library 1.1.1
37872  * Copyright(c) 2006-2007, Ext JS, LLC.
37873  *
37874  * Originally Released Under LGPL - original licence link has changed is not relivant.
37875  *
37876  * Fork - LGPL
37877  * <script type="text/javascript">
37878  */
37879
37880 // private - not really -- you end up using it !
37881 // This is a support class used internally by the Grid components
37882
37883 /**
37884  * @class Roo.grid.GridEditor
37885  * @extends Roo.Editor
37886  * Class for creating and editable grid elements.
37887  * @param {Object} config any settings (must include field)
37888  */
37889 Roo.grid.GridEditor = function(field, config){
37890     if (!config && field.field) {
37891         config = field;
37892         field = Roo.factory(config.field, Roo.form);
37893     }
37894     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
37895     field.monitorTab = false;
37896 };
37897
37898 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
37899     
37900     /**
37901      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
37902      */
37903     
37904     alignment: "tl-tl",
37905     autoSize: "width",
37906     hideEl : false,
37907     cls: "x-small-editor x-grid-editor",
37908     shim:false,
37909     shadow:"frame"
37910 });/*
37911  * Based on:
37912  * Ext JS Library 1.1.1
37913  * Copyright(c) 2006-2007, Ext JS, LLC.
37914  *
37915  * Originally Released Under LGPL - original licence link has changed is not relivant.
37916  *
37917  * Fork - LGPL
37918  * <script type="text/javascript">
37919  */
37920   
37921
37922   
37923 Roo.grid.PropertyRecord = Roo.data.Record.create([
37924     {name:'name',type:'string'},  'value'
37925 ]);
37926
37927
37928 Roo.grid.PropertyStore = function(grid, source){
37929     this.grid = grid;
37930     this.store = new Roo.data.Store({
37931         recordType : Roo.grid.PropertyRecord
37932     });
37933     this.store.on('update', this.onUpdate,  this);
37934     if(source){
37935         this.setSource(source);
37936     }
37937     Roo.grid.PropertyStore.superclass.constructor.call(this);
37938 };
37939
37940
37941
37942 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
37943     setSource : function(o){
37944         this.source = o;
37945         this.store.removeAll();
37946         var data = [];
37947         for(var k in o){
37948             if(this.isEditableValue(o[k])){
37949                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
37950             }
37951         }
37952         this.store.loadRecords({records: data}, {}, true);
37953     },
37954
37955     onUpdate : function(ds, record, type){
37956         if(type == Roo.data.Record.EDIT){
37957             var v = record.data['value'];
37958             var oldValue = record.modified['value'];
37959             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
37960                 this.source[record.id] = v;
37961                 record.commit();
37962                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
37963             }else{
37964                 record.reject();
37965             }
37966         }
37967     },
37968
37969     getProperty : function(row){
37970        return this.store.getAt(row);
37971     },
37972
37973     isEditableValue: function(val){
37974         if(val && val instanceof Date){
37975             return true;
37976         }else if(typeof val == 'object' || typeof val == 'function'){
37977             return false;
37978         }
37979         return true;
37980     },
37981
37982     setValue : function(prop, value){
37983         this.source[prop] = value;
37984         this.store.getById(prop).set('value', value);
37985     },
37986
37987     getSource : function(){
37988         return this.source;
37989     }
37990 });
37991
37992 Roo.grid.PropertyColumnModel = function(grid, store){
37993     this.grid = grid;
37994     var g = Roo.grid;
37995     g.PropertyColumnModel.superclass.constructor.call(this, [
37996         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37997         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37998     ]);
37999     this.store = store;
38000     this.bselect = Roo.DomHelper.append(document.body, {
38001         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
38002             {tag: 'option', value: 'true', html: 'true'},
38003             {tag: 'option', value: 'false', html: 'false'}
38004         ]
38005     });
38006     Roo.id(this.bselect);
38007     var f = Roo.form;
38008     this.editors = {
38009         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
38010         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
38011         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
38012         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
38013         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
38014     };
38015     this.renderCellDelegate = this.renderCell.createDelegate(this);
38016     this.renderPropDelegate = this.renderProp.createDelegate(this);
38017 };
38018
38019 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
38020     
38021     
38022     nameText : 'Name',
38023     valueText : 'Value',
38024     
38025     dateFormat : 'm/j/Y',
38026     
38027     
38028     renderDate : function(dateVal){
38029         return dateVal.dateFormat(this.dateFormat);
38030     },
38031
38032     renderBool : function(bVal){
38033         return bVal ? 'true' : 'false';
38034     },
38035
38036     isCellEditable : function(colIndex, rowIndex){
38037         return colIndex == 1;
38038     },
38039
38040     getRenderer : function(col){
38041         return col == 1 ?
38042             this.renderCellDelegate : this.renderPropDelegate;
38043     },
38044
38045     renderProp : function(v){
38046         return this.getPropertyName(v);
38047     },
38048
38049     renderCell : function(val){
38050         var rv = val;
38051         if(val instanceof Date){
38052             rv = this.renderDate(val);
38053         }else if(typeof val == 'boolean'){
38054             rv = this.renderBool(val);
38055         }
38056         return Roo.util.Format.htmlEncode(rv);
38057     },
38058
38059     getPropertyName : function(name){
38060         var pn = this.grid.propertyNames;
38061         return pn && pn[name] ? pn[name] : name;
38062     },
38063
38064     getCellEditor : function(colIndex, rowIndex){
38065         var p = this.store.getProperty(rowIndex);
38066         var n = p.data['name'], val = p.data['value'];
38067         
38068         if(typeof(this.grid.customEditors[n]) == 'string'){
38069             return this.editors[this.grid.customEditors[n]];
38070         }
38071         if(typeof(this.grid.customEditors[n]) != 'undefined'){
38072             return this.grid.customEditors[n];
38073         }
38074         if(val instanceof Date){
38075             return this.editors['date'];
38076         }else if(typeof val == 'number'){
38077             return this.editors['number'];
38078         }else if(typeof val == 'boolean'){
38079             return this.editors['boolean'];
38080         }else{
38081             return this.editors['string'];
38082         }
38083     }
38084 });
38085
38086 /**
38087  * @class Roo.grid.PropertyGrid
38088  * @extends Roo.grid.EditorGrid
38089  * This class represents the  interface of a component based property grid control.
38090  * <br><br>Usage:<pre><code>
38091  var grid = new Roo.grid.PropertyGrid("my-container-id", {
38092       
38093  });
38094  // set any options
38095  grid.render();
38096  * </code></pre>
38097   
38098  * @constructor
38099  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
38100  * The container MUST have some type of size defined for the grid to fill. The container will be
38101  * automatically set to position relative if it isn't already.
38102  * @param {Object} config A config object that sets properties on this grid.
38103  */
38104 Roo.grid.PropertyGrid = function(container, config){
38105     config = config || {};
38106     var store = new Roo.grid.PropertyStore(this);
38107     this.store = store;
38108     var cm = new Roo.grid.PropertyColumnModel(this, store);
38109     store.store.sort('name', 'ASC');
38110     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
38111         ds: store.store,
38112         cm: cm,
38113         enableColLock:false,
38114         enableColumnMove:false,
38115         stripeRows:false,
38116         trackMouseOver: false,
38117         clicksToEdit:1
38118     }, config));
38119     this.getGridEl().addClass('x-props-grid');
38120     this.lastEditRow = null;
38121     this.on('columnresize', this.onColumnResize, this);
38122     this.addEvents({
38123          /**
38124              * @event beforepropertychange
38125              * Fires before a property changes (return false to stop?)
38126              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38127              * @param {String} id Record Id
38128              * @param {String} newval New Value
38129          * @param {String} oldval Old Value
38130              */
38131         "beforepropertychange": true,
38132         /**
38133              * @event propertychange
38134              * Fires after a property changes
38135              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38136              * @param {String} id Record Id
38137              * @param {String} newval New Value
38138          * @param {String} oldval Old Value
38139              */
38140         "propertychange": true
38141     });
38142     this.customEditors = this.customEditors || {};
38143 };
38144 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
38145     
38146      /**
38147      * @cfg {Object} customEditors map of colnames=> custom editors.
38148      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
38149      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
38150      * false disables editing of the field.
38151          */
38152     
38153       /**
38154      * @cfg {Object} propertyNames map of property Names to their displayed value
38155          */
38156     
38157     render : function(){
38158         Roo.grid.PropertyGrid.superclass.render.call(this);
38159         this.autoSize.defer(100, this);
38160     },
38161
38162     autoSize : function(){
38163         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
38164         if(this.view){
38165             this.view.fitColumns();
38166         }
38167     },
38168
38169     onColumnResize : function(){
38170         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
38171         this.autoSize();
38172     },
38173     /**
38174      * Sets the data for the Grid
38175      * accepts a Key => Value object of all the elements avaiable.
38176      * @param {Object} data  to appear in grid.
38177      */
38178     setSource : function(source){
38179         this.store.setSource(source);
38180         //this.autoSize();
38181     },
38182     /**
38183      * Gets all the data from the grid.
38184      * @return {Object} data  data stored in grid
38185      */
38186     getSource : function(){
38187         return this.store.getSource();
38188     }
38189 });/*
38190  * Based on:
38191  * Ext JS Library 1.1.1
38192  * Copyright(c) 2006-2007, Ext JS, LLC.
38193  *
38194  * Originally Released Under LGPL - original licence link has changed is not relivant.
38195  *
38196  * Fork - LGPL
38197  * <script type="text/javascript">
38198  */
38199  
38200 /**
38201  * @class Roo.LoadMask
38202  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38203  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38204  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38205  * element's UpdateManager load indicator and will be destroyed after the initial load.
38206  * @constructor
38207  * Create a new LoadMask
38208  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38209  * @param {Object} config The config object
38210  */
38211 Roo.LoadMask = function(el, config){
38212     this.el = Roo.get(el);
38213     Roo.apply(this, config);
38214     if(this.store){
38215         this.store.on('beforeload', this.onBeforeLoad, this);
38216         this.store.on('load', this.onLoad, this);
38217         this.store.on('loadexception', this.onLoadException, this);
38218         this.removeMask = false;
38219     }else{
38220         var um = this.el.getUpdateManager();
38221         um.showLoadIndicator = false; // disable the default indicator
38222         um.on('beforeupdate', this.onBeforeLoad, this);
38223         um.on('update', this.onLoad, this);
38224         um.on('failure', this.onLoad, this);
38225         this.removeMask = true;
38226     }
38227 };
38228
38229 Roo.LoadMask.prototype = {
38230     /**
38231      * @cfg {Boolean} removeMask
38232      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38233      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38234      */
38235     /**
38236      * @cfg {String} msg
38237      * The text to display in a centered loading message box (defaults to 'Loading...')
38238      */
38239     msg : 'Loading...',
38240     /**
38241      * @cfg {String} msgCls
38242      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38243      */
38244     msgCls : 'x-mask-loading',
38245
38246     /**
38247      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38248      * @type Boolean
38249      */
38250     disabled: false,
38251
38252     /**
38253      * Disables the mask to prevent it from being displayed
38254      */
38255     disable : function(){
38256        this.disabled = true;
38257     },
38258
38259     /**
38260      * Enables the mask so that it can be displayed
38261      */
38262     enable : function(){
38263         this.disabled = false;
38264     },
38265     
38266     onLoadException : function()
38267     {
38268         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38269             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38270         }
38271         this.el.unmask(this.removeMask);
38272     },
38273     // private
38274     onLoad : function()
38275     {
38276         this.el.unmask(this.removeMask);
38277     },
38278
38279     // private
38280     onBeforeLoad : function(){
38281         if(!this.disabled){
38282             this.el.mask(this.msg, this.msgCls);
38283         }
38284     },
38285
38286     // private
38287     destroy : function(){
38288         if(this.store){
38289             this.store.un('beforeload', this.onBeforeLoad, this);
38290             this.store.un('load', this.onLoad, this);
38291             this.store.un('loadexception', this.onLoadException, this);
38292         }else{
38293             var um = this.el.getUpdateManager();
38294             um.un('beforeupdate', this.onBeforeLoad, this);
38295             um.un('update', this.onLoad, this);
38296             um.un('failure', this.onLoad, this);
38297         }
38298     }
38299 };/*
38300  * Based on:
38301  * Ext JS Library 1.1.1
38302  * Copyright(c) 2006-2007, Ext JS, LLC.
38303  *
38304  * Originally Released Under LGPL - original licence link has changed is not relivant.
38305  *
38306  * Fork - LGPL
38307  * <script type="text/javascript">
38308  */
38309
38310
38311 /**
38312  * @class Roo.XTemplate
38313  * @extends Roo.Template
38314  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38315 <pre><code>
38316 var t = new Roo.XTemplate(
38317         '&lt;select name="{name}"&gt;',
38318                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38319         '&lt;/select&gt;'
38320 );
38321  
38322 // then append, applying the master template values
38323  </code></pre>
38324  *
38325  * Supported features:
38326  *
38327  *  Tags:
38328
38329 <pre><code>
38330       {a_variable} - output encoded.
38331       {a_variable.format:("Y-m-d")} - call a method on the variable
38332       {a_variable:raw} - unencoded output
38333       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38334       {a_variable:this.method_on_template(...)} - call a method on the template object.
38335  
38336 </code></pre>
38337  *  The tpl tag:
38338 <pre><code>
38339         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38340         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38341         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38342         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38343   
38344         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38345         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38346 </code></pre>
38347  *      
38348  */
38349 Roo.XTemplate = function()
38350 {
38351     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38352     if (this.html) {
38353         this.compile();
38354     }
38355 };
38356
38357
38358 Roo.extend(Roo.XTemplate, Roo.Template, {
38359
38360     /**
38361      * The various sub templates
38362      */
38363     tpls : false,
38364     /**
38365      *
38366      * basic tag replacing syntax
38367      * WORD:WORD()
38368      *
38369      * // you can fake an object call by doing this
38370      *  x.t:(test,tesT) 
38371      * 
38372      */
38373     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38374
38375     /**
38376      * compile the template
38377      *
38378      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38379      *
38380      */
38381     compile: function()
38382     {
38383         var s = this.html;
38384      
38385         s = ['<tpl>', s, '</tpl>'].join('');
38386     
38387         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38388             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38389             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38390             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38391             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38392             m,
38393             id     = 0,
38394             tpls   = [];
38395     
38396         while(true == !!(m = s.match(re))){
38397             var forMatch   = m[0].match(nameRe),
38398                 ifMatch   = m[0].match(ifRe),
38399                 execMatch   = m[0].match(execRe),
38400                 namedMatch   = m[0].match(namedRe),
38401                 
38402                 exp  = null, 
38403                 fn   = null,
38404                 exec = null,
38405                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38406                 
38407             if (ifMatch) {
38408                 // if - puts fn into test..
38409                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38410                 if(exp){
38411                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38412                 }
38413             }
38414             
38415             if (execMatch) {
38416                 // exec - calls a function... returns empty if true is  returned.
38417                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38418                 if(exp){
38419                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38420                 }
38421             }
38422             
38423             
38424             if (name) {
38425                 // for = 
38426                 switch(name){
38427                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38428                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38429                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38430                 }
38431             }
38432             var uid = namedMatch ? namedMatch[1] : id;
38433             
38434             
38435             tpls.push({
38436                 id:     namedMatch ? namedMatch[1] : id,
38437                 target: name,
38438                 exec:   exec,
38439                 test:   fn,
38440                 body:   m[1] || ''
38441             });
38442             if (namedMatch) {
38443                 s = s.replace(m[0], '');
38444             } else { 
38445                 s = s.replace(m[0], '{xtpl'+ id + '}');
38446             }
38447             ++id;
38448         }
38449         this.tpls = [];
38450         for(var i = tpls.length-1; i >= 0; --i){
38451             this.compileTpl(tpls[i]);
38452             this.tpls[tpls[i].id] = tpls[i];
38453         }
38454         this.master = tpls[tpls.length-1];
38455         return this;
38456     },
38457     /**
38458      * same as applyTemplate, except it's done to one of the subTemplates
38459      * when using named templates, you can do:
38460      *
38461      * var str = pl.applySubTemplate('your-name', values);
38462      *
38463      * 
38464      * @param {Number} id of the template
38465      * @param {Object} values to apply to template
38466      * @param {Object} parent (normaly the instance of this object)
38467      */
38468     applySubTemplate : function(id, values, parent)
38469     {
38470         
38471         
38472         var t = this.tpls[id];
38473         
38474         
38475         try { 
38476             if(t.test && !t.test.call(this, values, parent)){
38477                 return '';
38478             }
38479         } catch(e) {
38480             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38481             Roo.log(e.toString());
38482             Roo.log(t.test);
38483             return ''
38484         }
38485         try { 
38486             
38487             if(t.exec && t.exec.call(this, values, parent)){
38488                 return '';
38489             }
38490         } catch(e) {
38491             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38492             Roo.log(e.toString());
38493             Roo.log(t.exec);
38494             return ''
38495         }
38496         try {
38497             var vs = t.target ? t.target.call(this, values, parent) : values;
38498             parent = t.target ? values : parent;
38499             if(t.target && vs instanceof Array){
38500                 var buf = [];
38501                 for(var i = 0, len = vs.length; i < len; i++){
38502                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38503                 }
38504                 return buf.join('');
38505             }
38506             return t.compiled.call(this, vs, parent);
38507         } catch (e) {
38508             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38509             Roo.log(e.toString());
38510             Roo.log(t.compiled);
38511             return '';
38512         }
38513     },
38514
38515     compileTpl : function(tpl)
38516     {
38517         var fm = Roo.util.Format;
38518         var useF = this.disableFormats !== true;
38519         var sep = Roo.isGecko ? "+" : ",";
38520         var undef = function(str) {
38521             Roo.log("Property not found :"  + str);
38522             return '';
38523         };
38524         
38525         var fn = function(m, name, format, args)
38526         {
38527             //Roo.log(arguments);
38528             args = args ? args.replace(/\\'/g,"'") : args;
38529             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38530             if (typeof(format) == 'undefined') {
38531                 format= 'htmlEncode';
38532             }
38533             if (format == 'raw' ) {
38534                 format = false;
38535             }
38536             
38537             if(name.substr(0, 4) == 'xtpl'){
38538                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38539             }
38540             
38541             // build an array of options to determine if value is undefined..
38542             
38543             // basically get 'xxxx.yyyy' then do
38544             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38545             //    (function () { Roo.log("Property not found"); return ''; })() :
38546             //    ......
38547             
38548             var udef_ar = [];
38549             var lookfor = '';
38550             Roo.each(name.split('.'), function(st) {
38551                 lookfor += (lookfor.length ? '.': '') + st;
38552                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38553             });
38554             
38555             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38556             
38557             
38558             if(format && useF){
38559                 
38560                 args = args ? ',' + args : "";
38561                  
38562                 if(format.substr(0, 5) != "this."){
38563                     format = "fm." + format + '(';
38564                 }else{
38565                     format = 'this.call("'+ format.substr(5) + '", ';
38566                     args = ", values";
38567                 }
38568                 
38569                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38570             }
38571              
38572             if (args.length) {
38573                 // called with xxyx.yuu:(test,test)
38574                 // change to ()
38575                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38576             }
38577             // raw.. - :raw modifier..
38578             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38579             
38580         };
38581         var body;
38582         // branched to use + in gecko and [].join() in others
38583         if(Roo.isGecko){
38584             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38585                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38586                     "';};};";
38587         }else{
38588             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38589             body.push(tpl.body.replace(/(\r\n|\n)/g,
38590                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38591             body.push("'].join('');};};");
38592             body = body.join('');
38593         }
38594         
38595         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38596        
38597         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38598         eval(body);
38599         
38600         return this;
38601     },
38602
38603     applyTemplate : function(values){
38604         return this.master.compiled.call(this, values, {});
38605         //var s = this.subs;
38606     },
38607
38608     apply : function(){
38609         return this.applyTemplate.apply(this, arguments);
38610     }
38611
38612  });
38613
38614 Roo.XTemplate.from = function(el){
38615     el = Roo.getDom(el);
38616     return new Roo.XTemplate(el.value || el.innerHTML);
38617 };/*
38618  * Original code for Roojs - LGPL
38619  * <script type="text/javascript">
38620  */
38621  
38622 /**
38623  * @class Roo.XComponent
38624  * A delayed Element creator...
38625  * Or a way to group chunks of interface together.
38626  * 
38627  * Mypart.xyx = new Roo.XComponent({
38628
38629     parent : 'Mypart.xyz', // empty == document.element.!!
38630     order : '001',
38631     name : 'xxxx'
38632     region : 'xxxx'
38633     disabled : function() {} 
38634      
38635     tree : function() { // return an tree of xtype declared components
38636         var MODULE = this;
38637         return 
38638         {
38639             xtype : 'NestedLayoutPanel',
38640             // technicall
38641         }
38642      ]
38643  *})
38644  *
38645  *
38646  * It can be used to build a big heiracy, with parent etc.
38647  * or you can just use this to render a single compoent to a dom element
38648  * MYPART.render(Roo.Element | String(id) | dom_element )
38649  * 
38650  * @extends Roo.util.Observable
38651  * @constructor
38652  * @param cfg {Object} configuration of component
38653  * 
38654  */
38655 Roo.XComponent = function(cfg) {
38656     Roo.apply(this, cfg);
38657     this.addEvents({ 
38658         /**
38659              * @event built
38660              * Fires when this the componnt is built
38661              * @param {Roo.XComponent} c the component
38662              */
38663         'built' : true
38664         
38665     });
38666     this.region = this.region || 'center'; // default..
38667     Roo.XComponent.register(this);
38668     this.modules = false;
38669     this.el = false; // where the layout goes..
38670     
38671     
38672 }
38673 Roo.extend(Roo.XComponent, Roo.util.Observable, {
38674     /**
38675      * @property el
38676      * The created element (with Roo.factory())
38677      * @type {Roo.Layout}
38678      */
38679     el  : false,
38680     
38681     /**
38682      * @property el
38683      * for BC  - use el in new code
38684      * @type {Roo.Layout}
38685      */
38686     panel : false,
38687     
38688     /**
38689      * @property layout
38690      * for BC  - use el in new code
38691      * @type {Roo.Layout}
38692      */
38693     layout : false,
38694     
38695      /**
38696      * @cfg {Function|boolean} disabled
38697      * If this module is disabled by some rule, return true from the funtion
38698      */
38699     disabled : false,
38700     
38701     /**
38702      * @cfg {String} parent 
38703      * Name of parent element which it get xtype added to..
38704      */
38705     parent: false,
38706     
38707     /**
38708      * @cfg {String} order
38709      * Used to set the order in which elements are created (usefull for multiple tabs)
38710      */
38711     
38712     order : false,
38713     /**
38714      * @cfg {String} name
38715      * String to display while loading.
38716      */
38717     name : false,
38718     /**
38719      * @cfg {String} region
38720      * Region to render component to (defaults to center)
38721      */
38722     region : 'center',
38723     
38724     /**
38725      * @cfg {Array} items
38726      * A single item array - the first element is the root of the tree..
38727      * It's done this way to stay compatible with the Xtype system...
38728      */
38729     items : false,
38730     
38731     /**
38732      * @property _tree
38733      * The method that retuns the tree of parts that make up this compoennt 
38734      * @type {function}
38735      */
38736     _tree  : false,
38737     
38738      /**
38739      * render
38740      * render element to dom or tree
38741      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
38742      */
38743     
38744     render : function(el)
38745     {
38746         
38747         el = el || false;
38748         var hp = this.parent ? 1 : 0;
38749         
38750         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
38751             // if parent is a '#.....' string, then let's use that..
38752             var ename = this.parent.substr(1)
38753             this.parent = false;
38754             el = Roo.get(ename);
38755             if (!el) {
38756                 Roo.log("Warning - element can not be found :#" + ename );
38757                 return;
38758             }
38759         }
38760         
38761         
38762         if (!this.parent) {
38763             
38764             el = el ? Roo.get(el) : false;      
38765             
38766             // it's a top level one..
38767             this.parent =  {
38768                 el : new Roo.BorderLayout(el || document.body, {
38769                 
38770                      center: {
38771                          titlebar: false,
38772                          autoScroll:false,
38773                          closeOnTab: true,
38774                          tabPosition: 'top',
38775                           //resizeTabs: true,
38776                          alwaysShowTabs: el && hp? false :  true,
38777                          hideTabs: el || !hp ? true :  false,
38778                          minTabWidth: 140
38779                      }
38780                  })
38781             }
38782         }
38783         
38784                 if (!this.parent.el) {
38785                         // probably an old style ctor, which has been disabled.
38786                         return;
38787                         
38788                 }
38789                 // The 'tree' method is  '_tree now' 
38790             
38791         var tree = this._tree ? this._tree() : this.tree();
38792         tree.region = tree.region || this.region;
38793         this.el = this.parent.el.addxtype(tree);
38794         this.fireEvent('built', this);
38795         
38796         this.panel = this.el;
38797         this.layout = this.panel.layout;
38798                 this.parentLayout = this.parent.layout  || false;  
38799          
38800     }
38801     
38802 });
38803
38804 Roo.apply(Roo.XComponent, {
38805     /**
38806      * @property  hideProgress
38807      * true to disable the building progress bar.. usefull on single page renders.
38808      * @type Boolean
38809      */
38810     hideProgress : false,
38811     /**
38812      * @property  buildCompleted
38813      * True when the builder has completed building the interface.
38814      * @type Boolean
38815      */
38816     buildCompleted : false,
38817      
38818     /**
38819      * @property  topModule
38820      * the upper most module - uses document.element as it's constructor.
38821      * @type Object
38822      */
38823      
38824     topModule  : false,
38825       
38826     /**
38827      * @property  modules
38828      * array of modules to be created by registration system.
38829      * @type {Array} of Roo.XComponent
38830      */
38831     
38832     modules : [],
38833     /**
38834      * @property  elmodules
38835      * array of modules to be created by which use #ID 
38836      * @type {Array} of Roo.XComponent
38837      */
38838      
38839     elmodules : [],
38840
38841     
38842     /**
38843      * Register components to be built later.
38844      *
38845      * This solves the following issues
38846      * - Building is not done on page load, but after an authentication process has occured.
38847      * - Interface elements are registered on page load
38848      * - Parent Interface elements may not be loaded before child, so this handles that..
38849      * 
38850      *
38851      * example:
38852      * 
38853      * MyApp.register({
38854           order : '000001',
38855           module : 'Pman.Tab.projectMgr',
38856           region : 'center',
38857           parent : 'Pman.layout',
38858           disabled : false,  // or use a function..
38859         })
38860      
38861      * * @param {Object} details about module
38862      */
38863     register : function(obj) {
38864                 
38865         Roo.XComponent.event.fireEvent('register', obj);
38866         switch(typeof(obj.disabled) ) {
38867                 
38868             case 'undefined':
38869                 break;
38870             
38871             case 'function':
38872                 if ( obj.disabled() ) {
38873                         return;
38874                 }
38875                 break;
38876             
38877             default:
38878                 if (obj.disabled) {
38879                         return;
38880                 }
38881                 break;
38882         }
38883                 
38884         this.modules.push(obj);
38885          
38886     },
38887     /**
38888      * convert a string to an object..
38889      * eg. 'AAA.BBB' -> finds AAA.BBB
38890
38891      */
38892     
38893     toObject : function(str)
38894     {
38895         if (!str || typeof(str) == 'object') {
38896             return str;
38897         }
38898         if (str.substring(0,1) == '#') {
38899             return str;
38900         }
38901
38902         var ar = str.split('.');
38903         var rt, o;
38904         rt = ar.shift();
38905             /** eval:var:o */
38906         try {
38907             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
38908         } catch (e) {
38909             throw "Module not found : " + str;
38910         }
38911         
38912         if (o === false) {
38913             throw "Module not found : " + str;
38914         }
38915         Roo.each(ar, function(e) {
38916             if (typeof(o[e]) == 'undefined') {
38917                 throw "Module not found : " + str;
38918             }
38919             o = o[e];
38920         });
38921         
38922         return o;
38923         
38924     },
38925     
38926     
38927     /**
38928      * move modules into their correct place in the tree..
38929      * 
38930      */
38931     preBuild : function ()
38932     {
38933         var _t = this;
38934         Roo.each(this.modules , function (obj)
38935         {
38936             Roo.XComponent.event.fireEvent('beforebuild', obj);
38937             
38938             var opar = obj.parent;
38939             try { 
38940                 obj.parent = this.toObject(opar);
38941             } catch(e) {
38942                 Roo.log("parent:toObject failed: " + e.toString());
38943                 return;
38944             }
38945             
38946             if (!obj.parent) {
38947                 Roo.debug && Roo.log("GOT top level module");
38948                 Roo.debug && Roo.log(obj);
38949                 obj.modules = new Roo.util.MixedCollection(false, 
38950                     function(o) { return o.order + '' }
38951                 );
38952                 this.topModule = obj;
38953                 return;
38954             }
38955                         // parent is a string (usually a dom element name..)
38956             if (typeof(obj.parent) == 'string') {
38957                 this.elmodules.push(obj);
38958                 return;
38959             }
38960             if (obj.parent.constructor != Roo.XComponent) {
38961                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
38962             }
38963             if (!obj.parent.modules) {
38964                 obj.parent.modules = new Roo.util.MixedCollection(false, 
38965                     function(o) { return o.order + '' }
38966                 );
38967             }
38968             if (obj.parent.disabled) {
38969                 obj.disabled = true;
38970             }
38971             obj.parent.modules.add(obj);
38972         }, this);
38973     },
38974     
38975      /**
38976      * make a list of modules to build.
38977      * @return {Array} list of modules. 
38978      */ 
38979     
38980     buildOrder : function()
38981     {
38982         var _this = this;
38983         var cmp = function(a,b) {   
38984             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
38985         };
38986         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
38987             throw "No top level modules to build";
38988         }
38989         
38990         // make a flat list in order of modules to build.
38991         var mods = this.topModule ? [ this.topModule ] : [];
38992                 
38993         // elmodules (is a list of DOM based modules )
38994         Roo.each(this.elmodules, function(e) {
38995             mods.push(e)
38996         });
38997
38998         
38999         // add modules to their parents..
39000         var addMod = function(m) {
39001             Roo.debug && Roo.log("build Order: add: " + m.name);
39002             
39003         mods.push(m);
39004         if (m.modules && !m.disabled) {
39005             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
39006             m.modules.keySort('ASC',  cmp );
39007             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
39008
39009             m.modules.each(addMod);
39010         } else {
39011             Roo.debug && Roo.log("build Order: no child modules");
39012             }
39013             // not sure if this is used any more..
39014             if (m.finalize) {
39015                 m.finalize.name = m.name + " (clean up) ";
39016                 mods.push(m.finalize);
39017             }
39018             
39019         }
39020         if (this.topModule) { 
39021             this.topModule.modules.keySort('ASC',  cmp );
39022             this.topModule.modules.each(addMod);
39023         }
39024         return mods;
39025     },
39026     
39027      /**
39028      * Build the registered modules.
39029      * @param {Object} parent element.
39030      * @param {Function} optional method to call after module has been added.
39031      * 
39032      */ 
39033    
39034     build : function() 
39035     {
39036         
39037         this.preBuild();
39038         var mods = this.buildOrder();
39039       
39040         //this.allmods = mods;
39041         //Roo.debug && Roo.log(mods);
39042         //return;
39043         if (!mods.length) { // should not happen
39044             throw "NO modules!!!";
39045         }
39046         
39047         
39048         var msg = "Building Interface...";
39049         // flash it up as modal - so we store the mask!?
39050         if (!this.hideProgress) {
39051             Roo.MessageBox.show({ title: 'loading' });
39052             Roo.MessageBox.show({
39053                title: "Please wait...",
39054                msg: msg,
39055                width:450,
39056                progress:true,
39057                closable:false,
39058                modal: false
39059               
39060             });
39061         }
39062         var total = mods.length;
39063         
39064         var _this = this;
39065         var progressRun = function() {
39066             if (!mods.length) {
39067                 Roo.debug && Roo.log('hide?');
39068                 if (!this.hideProgress) {
39069                     Roo.MessageBox.hide();
39070                 }
39071                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
39072                 
39073                 // THE END...
39074                 return false;   
39075             }
39076             
39077             var m = mods.shift();
39078             
39079             
39080             Roo.debug && Roo.log(m);
39081             // not sure if this is supported any more.. - modules that are are just function
39082             if (typeof(m) == 'function') { 
39083                 m.call(this);
39084                 return progressRun.defer(10, _this);
39085             } 
39086             
39087             
39088             msg = "Building Interface " + (total  - mods.length) + 
39089                     " of " + total + 
39090                     (m.name ? (' - ' + m.name) : '');
39091                         Roo.debug && Roo.log(msg);
39092             if (!this.hideProgress) { 
39093                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
39094             }
39095             
39096          
39097             // is the module disabled?
39098             var disabled = (typeof(m.disabled) == 'function') ?
39099                 m.disabled.call(m.module.disabled) : m.disabled;    
39100             
39101             
39102             if (disabled) {
39103                 return progressRun(); // we do not update the display!
39104             }
39105             
39106             // now build 
39107             
39108                         
39109                         
39110             m.render();
39111             // it's 10 on top level, and 1 on others??? why...
39112             return progressRun.defer(10, _this);
39113              
39114         }
39115         progressRun.defer(1, _this);
39116      
39117         
39118         
39119     },
39120         
39121         
39122         /**
39123          * Event Object.
39124          *
39125          *
39126          */
39127         event: false, 
39128     /**
39129          * wrapper for event.on - aliased later..  
39130          * Typically use to register a event handler for register:
39131          *
39132          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
39133          *
39134          */
39135     on : false
39136    
39137     
39138     
39139 });
39140
39141 Roo.XComponent.event = new Roo.util.Observable({
39142                 events : { 
39143                         /**
39144                          * @event register
39145                          * Fires when an Component is registered,
39146                          * set the disable property on the Component to stop registration.
39147                          * @param {Roo.XComponent} c the component being registerd.
39148                          * 
39149                          */
39150                         'register' : true,
39151             /**
39152                          * @event beforebuild
39153                          * Fires before each Component is built
39154                          * can be used to apply permissions.
39155                          * @param {Roo.XComponent} c the component being registerd.
39156                          * 
39157                          */
39158                         'beforebuild' : true,
39159                         /**
39160                          * @event buildcomplete
39161                          * Fires on the top level element when all elements have been built
39162                          * @param {Roo.XComponent} the top level component.
39163                          */
39164                         'buildcomplete' : true
39165                         
39166                 }
39167 });
39168
39169 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
39170  //<script type="text/javascript">
39171
39172
39173 /**
39174  * @class Roo.Login
39175  * @extends Roo.LayoutDialog
39176  * A generic Login Dialog..... - only one needed in theory!?!?
39177  *
39178  * Fires XComponent builder on success...
39179  * 
39180  * Sends 
39181  *    username,password, lang = for login actions.
39182  *    check = 1 for periodic checking that sesion is valid.
39183  *    passwordRequest = email request password
39184  *    logout = 1 = to logout
39185  * 
39186  * Affects: (this id="????" elements)
39187  *   loading  (removed) (used to indicate application is loading)
39188  *   loading-mask (hides) (used to hide application when it's building loading)
39189  *   
39190  * 
39191  * Usage: 
39192  *    
39193  * 
39194  * Myapp.login = Roo.Login({
39195      url: xxxx,
39196    
39197      realm : 'Myapp', 
39198      
39199      
39200      method : 'POST',
39201      
39202      
39203      * 
39204  })
39205  * 
39206  * 
39207  * 
39208  **/
39209  
39210 Roo.Login = function(cfg)
39211 {
39212     this.addEvents({
39213         'refreshed' : true
39214     });
39215     
39216     Roo.apply(this,cfg);
39217     
39218     Roo.onReady(function() {
39219         this.onLoad();
39220     }, this);
39221     // call parent..
39222     
39223    
39224     Roo.Login.superclass.constructor.call(this, this);
39225     //this.addxtype(this.items[0]);
39226     
39227     
39228 }
39229
39230
39231 Roo.extend(Roo.Login, Roo.LayoutDialog, {
39232     
39233     /**
39234      * @cfg {String} method
39235      * Method used to query for login details.
39236      */
39237     
39238     method : 'POST',
39239     /**
39240      * @cfg {String} url
39241      * URL to query login data. - eg. baseURL + '/Login.php'
39242      */
39243     url : '',
39244     
39245     /**
39246      * @property user
39247      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
39248      * @type {Object} 
39249      */
39250     user : false,
39251     /**
39252      * @property checkFails
39253      * Number of times we have attempted to get authentication check, and failed.
39254      * @type {Number} 
39255      */
39256     checkFails : 0,
39257       /**
39258      * @property intervalID
39259      * The window interval that does the constant login checking.
39260      * @type {Number} 
39261      */
39262     intervalID : 0,
39263     
39264     
39265     onLoad : function() // called on page load...
39266     {
39267         // load 
39268          
39269         if (Roo.get('loading')) { // clear any loading indicator..
39270             Roo.get('loading').remove();
39271         }
39272         
39273         //this.switchLang('en'); // set the language to english..
39274        
39275         this.check({
39276             success:  function(response, opts)  {  // check successfull...
39277             
39278                 var res = this.processResponse(response);
39279                 this.checkFails =0;
39280                 if (!res.success) { // error!
39281                     this.checkFails = 5;
39282                     //console.log('call failure');
39283                     return this.failure(response,opts);
39284                 }
39285                 
39286                 if (!res.data.id) { // id=0 == login failure.
39287                     return this.show();
39288                 }
39289                 
39290                               
39291                         //console.log(success);
39292                 this.fillAuth(res.data);   
39293                 this.checkFails =0;
39294                 Roo.XComponent.build();
39295             },
39296             failure : this.show
39297         });
39298         
39299     }, 
39300     
39301     
39302     check: function(cfg) // called every so often to refresh cookie etc..
39303     {
39304         if (cfg.again) { // could be undefined..
39305             this.checkFails++;
39306         } else {
39307             this.checkFails = 0;
39308         }
39309         var _this = this;
39310         if (this.sending) {
39311             if ( this.checkFails > 4) {
39312                 Roo.MessageBox.alert("Error",  
39313                     "Error getting authentication status. - try reloading, or wait a while", function() {
39314                         _this.sending = false;
39315                     }); 
39316                 return;
39317             }
39318             cfg.again = true;
39319             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
39320             return;
39321         }
39322         this.sending = true;
39323         
39324         Roo.Ajax.request({  
39325             url: this.url,
39326             params: {
39327                 getAuthUser: true
39328             },  
39329             method: this.method,
39330             success:  cfg.success || this.success,
39331             failure : cfg.failure || this.failure,
39332             scope : this,
39333             callCfg : cfg
39334               
39335         });  
39336     }, 
39337     
39338     
39339     logout: function()
39340     {
39341         window.onbeforeunload = function() { }; // false does not work for IE..
39342         this.user = false;
39343         var _this = this;
39344         
39345         Roo.Ajax.request({  
39346             url: this.url,
39347             params: {
39348                 logout: 1
39349             },  
39350             method: 'GET',
39351             failure : function() {
39352                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
39353                     document.location = document.location.toString() + '?ts=' + Math.random();
39354                 });
39355                 
39356             },
39357             success : function() {
39358                 _this.user = false;
39359                 this.checkFails =0;
39360                 // fixme..
39361                 document.location = document.location.toString() + '?ts=' + Math.random();
39362             }
39363               
39364               
39365         }); 
39366     },
39367     
39368     processResponse : function (response)
39369     {
39370         var res = '';
39371         try {
39372             res = Roo.decode(response.responseText);
39373             // oops...
39374             if (typeof(res) != 'object') {
39375                 res = { success : false, errorMsg : res, errors : true };
39376             }
39377             if (typeof(res.success) == 'undefined') {
39378                 res.success = false;
39379             }
39380             
39381         } catch(e) {
39382             res = { success : false,  errorMsg : response.responseText, errors : true };
39383         }
39384         return res;
39385     },
39386     
39387     success : function(response, opts)  // check successfull...
39388     {  
39389         this.sending = false;
39390         var res = this.processResponse(response);
39391         if (!res.success) {
39392             return this.failure(response, opts);
39393         }
39394         if (!res.data || !res.data.id) {
39395             return this.failure(response,opts);
39396         }
39397         //console.log(res);
39398         this.fillAuth(res.data);
39399         
39400         this.checkFails =0;
39401         
39402     },
39403     
39404     
39405     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
39406     {
39407         this.authUser = -1;
39408         this.sending = false;
39409         var res = this.processResponse(response);
39410         //console.log(res);
39411         if ( this.checkFails > 2) {
39412         
39413             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
39414                 "Error getting authentication status. - try reloading"); 
39415             return;
39416         }
39417         opts.callCfg.again = true;
39418         this.check.defer(1000, this, [ opts.callCfg ]);
39419         return;  
39420     },
39421     
39422     
39423     
39424     fillAuth: function(au) {
39425         this.startAuthCheck();
39426         this.authUserId = au.id;
39427         this.authUser = au;
39428         this.lastChecked = new Date();
39429         this.fireEvent('refreshed', au);
39430         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
39431         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
39432         au.lang = au.lang || 'en';
39433         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
39434         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
39435         this.switchLang(au.lang );
39436         
39437      
39438         // open system... - -on setyp..
39439         if (this.authUserId  < 0) {
39440             Roo.MessageBox.alert("Warning", 
39441                 "This is an open system - please set up a admin user with a password.");  
39442         }
39443          
39444         //Pman.onload(); // which should do nothing if it's a re-auth result...
39445         
39446              
39447     },
39448     
39449     startAuthCheck : function() // starter for timeout checking..
39450     {
39451         if (this.intervalID) { // timer already in place...
39452             return false;
39453         }
39454         var _this = this;
39455         this.intervalID =  window.setInterval(function() {
39456               _this.check(false);
39457             }, 120000); // every 120 secs = 2mins..
39458         
39459         
39460     },
39461          
39462     
39463     switchLang : function (lang) 
39464     {
39465         _T = typeof(_T) == 'undefined' ? false : _T;
39466           if (!_T || !lang.length) {
39467             return;
39468         }
39469         
39470         if (!_T && lang != 'en') {
39471             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
39472             return;
39473         }
39474         
39475         if (typeof(_T.en) == 'undefined') {
39476             _T.en = {};
39477             Roo.apply(_T.en, _T);
39478         }
39479         
39480         if (typeof(_T[lang]) == 'undefined') {
39481             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
39482             return;
39483         }
39484         
39485         
39486         Roo.apply(_T, _T[lang]);
39487         // just need to set the text values for everything...
39488         var _this = this;
39489         /* this will not work ...
39490         if (this.form) { 
39491             
39492                
39493             function formLabel(name, val) {
39494                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
39495             }
39496             
39497             formLabel('password', "Password"+':');
39498             formLabel('username', "Email Address"+':');
39499             formLabel('lang', "Language"+':');
39500             this.dialog.setTitle("Login");
39501             this.dialog.buttons[0].setText("Forgot Password");
39502             this.dialog.buttons[1].setText("Login");
39503         }
39504         */
39505         
39506         
39507     },
39508     
39509     
39510     title: "Login",
39511     modal: true,
39512     width:  350,
39513     //height: 230,
39514     height: 180,
39515     shadow: true,
39516     minWidth:200,
39517     minHeight:180,
39518     //proxyDrag: true,
39519     closable: false,
39520     draggable: false,
39521     collapsible: false,
39522     resizable: false,
39523     center: {  // needed??
39524         autoScroll:false,
39525         titlebar: false,
39526        // tabPosition: 'top',
39527         hideTabs: true,
39528         closeOnTab: true,
39529         alwaysShowTabs: false
39530     } ,
39531     listeners : {
39532         
39533         show  : function(dlg)
39534         {
39535             //console.log(this);
39536             this.form = this.layout.getRegion('center').activePanel.form;
39537             this.form.dialog = dlg;
39538             this.buttons[0].form = this.form;
39539             this.buttons[0].dialog = dlg;
39540             this.buttons[1].form = this.form;
39541             this.buttons[1].dialog = dlg;
39542            
39543            //this.resizeToLogo.defer(1000,this);
39544             // this is all related to resizing for logos..
39545             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
39546            //// if (!sz) {
39547              //   this.resizeToLogo.defer(1000,this);
39548              //   return;
39549            // }
39550             //var w = Ext.lib.Dom.getViewWidth() - 100;
39551             //var h = Ext.lib.Dom.getViewHeight() - 100;
39552             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
39553             //this.center();
39554             if (this.disabled) {
39555                 this.hide();
39556                 return;
39557             }
39558             
39559             if (this.user.id < 0) { // used for inital setup situations.
39560                 return;
39561             }
39562             
39563             if (this.intervalID) {
39564                 // remove the timer
39565                 window.clearInterval(this.intervalID);
39566                 this.intervalID = false;
39567             }
39568             
39569             
39570             if (Roo.get('loading')) {
39571                 Roo.get('loading').remove();
39572             }
39573             if (Roo.get('loading-mask')) {
39574                 Roo.get('loading-mask').hide();
39575             }
39576             
39577             //incomming._node = tnode;
39578             this.form.reset();
39579             //this.dialog.modal = !modal;
39580             //this.dialog.show();
39581             this.el.unmask(); 
39582             
39583             
39584             this.form.setValues({
39585                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
39586                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
39587             });
39588             
39589             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
39590             if (this.form.findField('username').getValue().length > 0 ){
39591                 this.form.findField('password').focus();
39592             } else {
39593                this.form.findField('username').focus();
39594             }
39595     
39596         }
39597     },
39598     items : [
39599          {
39600        
39601             xtype : 'ContentPanel',
39602             xns : Roo,
39603             region: 'center',
39604             fitToFrame : true,
39605             
39606             items : [
39607     
39608                 {
39609                
39610                     xtype : 'Form',
39611                     xns : Roo.form,
39612                     labelWidth: 100,
39613                     style : 'margin: 10px;',
39614                     
39615                     listeners : {
39616                         actionfailed : function(f, act) {
39617                             // form can return { errors: .... }
39618                                 
39619                             //act.result.errors // invalid form element list...
39620                             //act.result.errorMsg// invalid form element list...
39621                             
39622                             this.dialog.el.unmask();
39623                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
39624                                         "Login failed - communication error - try again.");
39625                                       
39626                         },
39627                         actioncomplete: function(re, act) {
39628                              
39629                             Roo.state.Manager.set(
39630                                 this.dialog.realm + '.username',  
39631                                     this.findField('username').getValue()
39632                             );
39633                             Roo.state.Manager.set(
39634                                 this.dialog.realm + '.lang',  
39635                                 this.findField('lang').getValue() 
39636                             );
39637                             
39638                             this.dialog.fillAuth(act.result.data);
39639                               
39640                             this.dialog.hide();
39641                             
39642                             if (Roo.get('loading-mask')) {
39643                                 Roo.get('loading-mask').show();
39644                             }
39645                             Roo.XComponent.build();
39646                             
39647                              
39648                             
39649                         }
39650                     },
39651                     items : [
39652                         {
39653                             xtype : 'TextField',
39654                             xns : Roo.form,
39655                             fieldLabel: "Email Address",
39656                             name: 'username',
39657                             width:200,
39658                             autoCreate : {tag: "input", type: "text", size: "20"}
39659                         },
39660                         {
39661                             xtype : 'TextField',
39662                             xns : Roo.form,
39663                             fieldLabel: "Password",
39664                             inputType: 'password',
39665                             name: 'password',
39666                             width:200,
39667                             autoCreate : {tag: "input", type: "text", size: "20"},
39668                             listeners : {
39669                                 specialkey : function(e,ev) {
39670                                     if (ev.keyCode == 13) {
39671                                         this.form.dialog.el.mask("Logging in");
39672                                         this.form.doAction('submit', {
39673                                             url: this.form.dialog.url,
39674                                             method: this.form.dialog.method
39675                                         });
39676                                     }
39677                                 }
39678                             }  
39679                         },
39680                         {
39681                             xtype : 'ComboBox',
39682                             xns : Roo.form,
39683                             fieldLabel: "Language",
39684                             name : 'langdisp',
39685                             store: {
39686                                 xtype : 'SimpleStore',
39687                                 fields: ['lang', 'ldisp'],
39688                                 data : [
39689                                     [ 'en', 'English' ],
39690                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
39691                                     [ 'zh_CN', '\u7C21\u4E2D' ]
39692                                 ]
39693                             },
39694                             
39695                             valueField : 'lang',
39696                             hiddenName:  'lang',
39697                             width: 200,
39698                             displayField:'ldisp',
39699                             typeAhead: false,
39700                             editable: false,
39701                             mode: 'local',
39702                             triggerAction: 'all',
39703                             emptyText:'Select a Language...',
39704                             selectOnFocus:true,
39705                             listeners : {
39706                                 select :  function(cb, rec, ix) {
39707                                     this.form.switchLang(rec.data.lang);
39708                                 }
39709                             }
39710                         
39711                         }
39712                     ]
39713                 }
39714                   
39715                 
39716             ]
39717         }
39718     ],
39719     buttons : [
39720         {
39721             xtype : 'Button',
39722             xns : 'Roo',
39723             text : "Forgot Password",
39724             listeners : {
39725                 click : function() {
39726                     //console.log(this);
39727                     var n = this.form.findField('username').getValue();
39728                     if (!n.length) {
39729                         Roo.MessageBox.alert("Error", "Fill in your email address");
39730                         return;
39731                     }
39732                     Roo.Ajax.request({
39733                         url: this.dialog.url,
39734                         params: {
39735                             passwordRequest: n
39736                         },
39737                         method: this.dialog.method,
39738                         success:  function(response, opts)  {  // check successfull...
39739                         
39740                             var res = this.dialog.processResponse(response);
39741                             if (!res.success) { // error!
39742                                Roo.MessageBox.alert("Error" ,
39743                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
39744                                return;
39745                             }
39746                             Roo.MessageBox.alert("Notice" ,
39747                                 "Please check you email for the Password Reset message");
39748                         },
39749                         failure : function() {
39750                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
39751                         }
39752                         
39753                     });
39754                 }
39755             }
39756         },
39757         {
39758             xtype : 'Button',
39759             xns : 'Roo',
39760             text : "Login",
39761             listeners : {
39762                 
39763                 click : function () {
39764                         
39765                     this.dialog.el.mask("Logging in");
39766                     this.form.doAction('submit', {
39767                             url: this.dialog.url,
39768                             method: this.dialog.method
39769                     });
39770                 }
39771             }
39772         }
39773     ]
39774   
39775   
39776 })
39777  
39778
39779
39780